DataFilter Interferes with AutoComplete on UltraCombo Inside UltraGrid - vb.net

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.

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 ;)

Bound datagrid not showing values

I have a datagrid which is bound to a binding source which itself is populated from a list.
I can see that the binding source is populated and I can see that the datagrid has the expected number of rows to match the data in the list but i can not get the columns to populate with the data. I have tried setting the datapropertyname for the columns but to no avail. I know this is probably dead simple but I'm going round in circles.
Please can anyone help
Dim cE As New ClsEmail
Dim bs As New BindingSource
Dim dgv = Me.DataGridView1
dgv.AutoGenerateColumns = False
bs.DataSource = cE.GetMail("team#xxxx.xxxx")
With dgv.Columns(0)
.DataPropertyName = "ID"
.HeaderText = "ID"
End With
With dgv.Columns(1)
.DataPropertyName = "Subject"
.HeaderText = "Subject"
End With
dgv.DataSource = bs
When i press the button to populate the datagrid I get four rows but no values in the columns. Clearly I'm not binding the columns correctly but i can't see what I'm missing.
List(Of T) is already bindable. Rearrange your properties in your Email class like this, as the DataGridView will create the columns in property order:
Public Class Email
Public Property ID As Integer
Public Property Subject As String
Public Property DateReceived As DateTime
End Class
Then, just set the datasource like this:
With DataGridView1
.AutoGenerateColumns = True
.DataSource = cE.GetMail("team#xxxx.xxxx")
.Columns(2).Visible = False ' DataReceived (if you really intended to hide this)
End With

How to increase display time of DataGridView's Column header tooltips?

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.

Unable to set valuemember/display member to bind combobox

I'm having trouble adding dynamically a combo box column to my datagridview (before you ask, yes it must be dynamic and not done in editor).
The main feature is that the combobox cell is different for each row, so it must be done using combo box cell. checkedRows is a datatable.
Name of the datagridview is editCameraTable. It already has a few columns at this point:
'create new column
Dim resComboColumn As New DataGridViewComboBoxColumn _
With {.HeaderText = "Resolution", .ReadOnly = False, .DisplayIndex = 7, .Name = "Resolution", _
.DisplayMember = "Name", .ValueMember = "ID", .DataPropertyName = "ID"}
'add combo box column
EditCameras.editCameraTable.Columns.Insert(17, resComboColumn)
addResCmbBox(checkedRows, resComboColumn)
Pretty straight forward. You'll notice I have the value member, dataproperty name, etc. Here's the addResCmbBox definition:
Public Function addResCmbBox(ByRef DT As DataTable, column As DataGridViewComboBoxColumn)
Dim resolutions As String()
'for each camera
For i As Integer = 0 To DT.Rows.Count - 1
Dim camera As camera = convertDTtoCam(DT, i)
'get the resarray
Select Case DT.Rows(i).Item("Maker").ToString.ToLower
Case "acti"
resolutions = ACTi.GetResArray(camera)
Case Else
resolutions = ACTi.GetResArray(camera)
End Select
'add items to combobox list
Dim comboCell As New DataGridViewComboBoxCell
comboCell.DataSource = resolutions
For j As Integer = 0 To resolutions.Length - 1
'set to current resolution value
If resolutions(j).TrimStart("N") = camera.res Then
comboCell.Value = resolutions(j)
End If
Next
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
EditCameras.editCameraTable("Resolution", i) = comboCell
Next
Return Nothing
End Function
camera is a structure. I have no problems until I get to the displayMember and value member problem, i.e. the last line starting with "editcameras.editcameratable...".
When doing so, the exception of "The Field Name does not exist" pops up. If I don't assign the displayMember and valueMember, I have no problems. But, I can't get the value selected in the comboBox (it comes back as Null). At runtime, the combobox column has the valuemember and display name as "ID" and "Name".
How can I bind this comboboxcell to the row so that I can later get it's selected value?
UPDATE:
I did as was commented, and created a struct/class that was meant to be the resolution property:
Public Class ResolutionStruct
Property Name As String
Property ID As String
End Class
And within the loop I create a list of this class, and assign the values to it:
Dim resolutionList As New List(Of ACTi.ResolutionStruct)
For j As Integer = 0 To resolutions.Length - 1
Dim resClass As New ACTi.ResolutionStruct
resClass.Name = resolutions(j)
resClass.ID = resolutions(j)
resolutionList.Add(resClass)
Next
'set combocell values
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
comboCell.DataSource = resolutionList
EditCameras.editCameraTable("Resolution", i) = comboCell
However, the comboboxCell doesn't show any value when it drops down. So, now I've bound the values but it shows nothing. Is there anything else I should be doing so that I get both the holy combo of seeing the values I'm picking AND having them be bound to the data grid view? :D
UPDATE 2
So, mea culpa. I was adding the combobox cell to the wrong column!
So now, it is showing the values. I click a value, and try to grab the selected value as as string:
Dim cmbbox2 As DataGridViewComboBoxCell = editCameraTable("Resolution", i)
resolution(i) = cmbbox2.Selected.ToString()
But it still says it's a null value! Mid build I checked the combobox props. IN fact "selected" is a boolean as false. It has no value, says it has no items as well. Any ideas on why it says it is null?
UPDATE3:
I recently resorted a different column in the table, and the values from the combo box are cleared! I guess it's really never being attached/bound in the first place.
UPDATE4:
Fixed it!!
Apparently this line:
editCameraTable.Sort(editCameraTable.Columns("ID"), System.ComponentModel.ListSortDirection.Ascending)
Caused the table to freak out! I can now get the value (woohoo!)
Right, I'll try to explain this shortly:
DisplayMember and ValueMember are supposed to be set using properties. For example you create a class containing Name and ID
Public Class Test
Property Name as String
Property ID as String
End Class
Create a few of these objects and put them in a list. Set the list as the datasource to the combobox. Now you can access the DisplayMember and ValueMember as you have written it in your code. Value would be the ID and SelectedItem would be the entire class.
What you are doing now is that you are adding a list of strings to the combobox. A String does not contain the Property Name nor ID, so naturally you can't fetch them. See it like this:
To be able to use Value and/or DisplayMember you need to be able to fetch the Property by yourself. In this case:
resolutions(j).Name
or
resolutions(j).ID
This does not work.
But for example you would be able to do this:
resolutions(j).Length
So You would be able to do this, which would display the Length in the combobox:
Combobox.DisplayMember = "Length"
To currently get the value you would have to do:
Combobox.SelectedItem.ToString()
But since you have it in a combobox column My guess is that this won't cut it since you can't fetch the value from the DataGridView.
EDIT: You are still doing this right?
<DataGridView>.Item("Resolution", i) = comboCell
Otherwise you will have empty comboboxes.
EDIT2: No need to fetch value from Combobox, get it from Grid cell instead:
<DataGridView>.Item("Resolution", i).Value
When creating the columns don't forget to set a defaultvalue to the combobox, otherwise it might be Nothing:
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
comboCell.Value = resolutions(0)

Can I create a property of a property?

So I recently grasped the concept of using classes in my Visual Basic programming, and I found it tremendously helpful. In my current project, I have several groups boxes of check boxes (each check box denotes a "Behavior") and in each group box, there is always one check box that has a textbox control instead of a label (to allow the user to specify an "Other" behavior). It is that user-generated label that is giving me trouble...
I created a class called "Behaviors" that basically does the following:
getChecked > This method gets each checked checkbox and adds it to
the BehaviorCollection for a given Form.
behaviorCollection > represents the collection of checked
checkboxes.
getOtherChecked > does the same as "getChecked" except with the
"Other Behavior" checkboxes.
otherBehaviorCollection > represents the collection of checked
"Other" checkboxes.
The issue is that for each checked "Other Behaviors" checkbox, I need to store the value of its corresponding textbox. I would like to set my getOtherChecked() method to do this, so that in the end, I would be able to something like this...
Dim myBoxes as new Behaviors
Dim cBox as Checkbox
Dim cBoxLabel as String
myBoxes.getOtherChecked(myUserForm) 'This would get each checked "Other Behaviors" checkbox object, and also somehow add another property to it called "LinkedTextboxLabel" that would be assigned the value of the corresponding textbox.
cBox = myBoxes.otherBehaviorCollection.item(0) 'Assign a checkbox from my "Other Behaviors" collection to a variable.
cBoxLabel = cBox.LinkedTextboxLabel 'Assign the user-inputted value of the linked textbox to a variable.
So basically how could/should I add a custom-property to a collection item or checkbox?
I thought about just adding the names of the controls to a temporary DataTable or SQL table, so that each row would have the name of a checkbox in one column and its corresponding textbox value in the next, but I am hoping there is a more commonly used and accepted method.
Thank you in advance!
You could add a property for the text associated with the "Other Behaviors" checkbox.
EDIT: You might be trying to generalize your data too far, because the "Other behaviors" is a special case and deserves separate consideration.
If you have a look at what the following code (in a new Windows Forms project) creates, it might give you ideas:
Public Class Form1
''' <summary>
''' A behaviour domain and its characteristics, with one user-defined entry.
''' </summary>
''' <remarks></remarks>
Public Class BehavioursSectionDescriptor
Property BehaviourTypeName As String
Property BehaviourNames As List(Of String)
Property CustomBehaviours As String
End Class
''' <summary>
''' Return a GroupBox containing CheckBoxes and one Checkbox with a TextBox adjacent to it.
''' </summary>
''' <param name="behaviourSet"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function GetBehaviourGroupPanel(behaviourSet As BehavioursSectionDescriptor) As GroupBox
Dim gb As New GroupBox
gb.Text = behaviourSet.BehaviourTypeName
Dim fixedBehaviourNames As List(Of String) = behaviourSet.BehaviourNames
Dim customBehavioursValue As String = behaviourSet.CustomBehaviours
Dim cbVertSeparation As Integer = 4
Dim gbPadding As Integer = 20
Dim cb As New CheckBox
Dim yLoc As Integer = gbPadding
For i = 0 To fixedBehaviourNames.Count - 1
cb = New CheckBox
cb.Location = New Point(gbPadding, yLoc)
cb.Text = fixedBehaviourNames(i)
' you can use the .Tag Object of a Control to store information
cb.Tag = behaviourSet.BehaviourTypeName & "-Cb-" & i.ToString()
gb.Controls.Add(cb)
yLoc += cb.Height + cbVertSeparation
Next
cb = New CheckBox
cb.Text = ""
cb.Location = New Point(gbPadding, yLoc)
cb.Tag = behaviourSet.BehaviourTypeName & "-Custom behaviours"
gb.Controls.Add(cb)
Dim tb As New TextBox
tb.Location = New Point(gbPadding + 18, yLoc)
tb.Width = 100
tb.Text = customBehavioursValue
gb.Controls.Add(tb)
' make sure the textbox appears in front of the checkbox's label area
tb.BringToFront()
gb.Size = New Size(160, yLoc + gbPadding * 2)
Return gb
End Function
Private Function GetTestData() As List(Of BehavioursSectionDescriptor)
Dim bsds = New List(Of BehavioursSectionDescriptor)
bsds.Add(New BehavioursSectionDescriptor With {.BehaviourTypeName = "In water", _
.BehaviourNames = New List(Of String) From {"Floats", "Spins"}, _
.CustomBehaviours = "Paddles"})
bsds.Add(New BehavioursSectionDescriptor With {.BehaviourTypeName = "Under light", _
.BehaviourNames = New List(Of String) From {"Shines", "Glows", "Reflects"}, _
.CustomBehaviours = "Iridesces"})
bsds.Add(New BehavioursSectionDescriptor With {.BehaviourTypeName = "Near food", _
.BehaviourNames = New List(Of String) From {"Sniffs", "Looks"}, _
.CustomBehaviours = ""})
Return bsds
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim bsds As List(Of BehavioursSectionDescriptor) = GetTestData()
Dim gbs As New List(Of GroupBox)
Dim xLoc As Integer = 20
Dim yLoc As Integer = 20
' make some GroupBoxes to present the data input fields
For i = 0 To bsds.Count - 1
Dim gb = GetBehaviourGroupPanel(bsds(i))
gb.Location = New Point(xLoc, yLoc)
gb.Dock = DockStyle.None
yLoc += gb.Height + 30
Me.Controls.Add(gb)
Next
' size the form to fit the content
Me.Size = New Size(240, yLoc + 40)
End Sub
End Class
I know it doesn't answer the question of adding a property to a property, but could you create a class for the Other checkbox and override it's capabilities? Then you could add checkboxes and OtherCheckBoxes to your generic collection? for instance, (by no means complete, but you should get the idea)
EDIT: Changed code to show Shadows
Public Class OptionalCheckbox : Inherits CheckBox
Private mOptionalText As String
Public Shadows Property Text() As String
Get
Return mOptionalText
End Get
Set(value As String)
mOptionalText = value
MyBase.Text = value
End Set
End Property
End Class
For each item, if you were to retrieve .Text, you would either get your textbox value or your checkbox label (if it was a normal checkbox)
And how to utilize in other parts of your code. Again, this is just more of an example. You would still need to work with the textbox that is assigned to the OtherCheckBox to get it to write the text to that, as well as read from that into the .Text property of the Class.
Dim newCheckBoxCollection As New Collection
Dim cBox As New CheckBox
cBox.Text = "Standard Value Here"
'other properties of the checkbox can be modified here
newCheckBoxCollection.Add(cBox)
Dim cOBox As New OptionalCheckbox
cOBox.Text = "Optional Text Here"
'other properties of the checkbox can be modified here
newCheckBoxCollection.Add(cOBox)
For Each cb As CheckBox In newCheckBoxCollection
Me.FlowLayoutPanel1.Controls.Add(cb)
Next
If you are trying to just save the data into something like a DataTable or SQL table the code would be a bit of an overkill. I suggest you use a stream reader/writer and try checking the values that way as the code would be a lot more simple.