How to add a UserControl to a DataGridView in VB.net, and have the control always showing? - vb.net

I made a usercontrol that is basically a panel with a bunch of labels, buttons, and Event Handlers on it. I want to be able to populate a DataGridView with these controls.
I have followed the instructions in this MSDN article, and have created a DataGridViewColumn and a DataGridViewCell class to associate with my Control.
The issue I am having is that the control is not rendering. When I run the code at the link above, It shows the date in the cell, and the user control only shows when the cell is being edited. What I'd like to have happen is that the control is always visible in the cell, similar to the behavior in a DataGridViewImageColumn.
How can I force the DataGridView to always show the control?

The main idea:
Create a user control
In form load event, for each record, create an instance of user control, set values to its properties, add event handlers, set its visiblity to false and then add it to your grid Controls collection, and set the tag of your wanted cell or that row to this control.
Handle CellPainting event of your grid and for each row of grid, retrieve tag of your wanted cell and convert it to your control and position it in cell bounds and make it visible. To get the position that control should be shown use GetCellDisplayRectangle method of DataGridView
Screenshot:
My sample control with name of SomeControl, contains a label and a text box and a property SomeProperty and an event SomePropertyChanges. I show cell value in TextBox of my custom control.
You can do anything with your custom control containing any control and any type of properties and events.
Code:
Here is vb-like pseudo code.
'This is not VB, this is vb-like pseudo code
Private Sub Form_Load(sender as object , e as EventArgs)
'Get data
'Show data in grid
'For each row
For Each row as DataGridViewRow in this.categoryDataGridView.Rows
'Create an instance of control
Dim myControl as New SomeControl()
'Set properties and register event handlers
myControl.SomeProperty = row.Cells[2].Value
myControl.SomePropertyChanged += myControl_SomePropertyChanged
'Make it invisible
myControl.Visible = False
'Set tag of your wanted cell to control
row.Cells[2].Tag = myControl
'Add control to Controls collection of grid
this.categoryDataGridView.Controls.Add(myControl)
Next
End Sub
Private Sub myControl_SomePropertyChanged(sender as object, e as EventArgs)
'event handler of SomePropertyChanged for custom control
'do stuff here
End Sub
Private Sub dataGridView_CellPainting(sender as object, e as DataGridViewCellPaintingEventArgs )
'for each row
For i as Integer=0 To dataGridView.RowCount- 1
'Extract control from tag of your wanted cell
var myControl= this.dataGridView.Rows[i].Cells[2].Tag as SomeControl
'Get cell rectangle
Dim cellRectangle As Rectangle= dataGridView.GetCellDisplayRectangle(2, i, True)
'Set location
myControl.Location = New Point(cellRectangle.X, cellRectangle.Y )
'Set size
myControl.Size = New Size(cellRectangle.Width - 1, cellRectangle.Height - 1)
'Make visible
myControl.Visible = True
Next
}

read this article
How can you add a custom user control to a datagridview?
http://bytes.com/topic/visual-basic-net/answers/444528-how-can-you-add-custom-user-control-datagridview

private void button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Email Id");
for (int j = 0; j < 10; j++)
{
dt.Rows.Add("", "ravindra.m089#gmail.com");
}
this.dataGridView1.DataSource = dt;
this.dataGridView1.Columns[0].Width = 200;
/*
* First method : Convert to an existed cell type such ComboBox cell,etc
*/
DataGridViewComboBoxCell ComboBoxCell = new DataGridViewComboBoxCell();
ComboBoxCell.Items.AddRange(new string[] { "aaa", "bbb", "ccc" });
this.dataGridView1[0, 0] = ComboBoxCell;
this.dataGridView1[0, 0].Value = "bbb";
DataGridViewTextBoxCell TextBoxCell = new DataGridViewTextBoxCell();
this.dataGridView1[0, 1] = TextBoxCell;
this.dataGridView1[0, 1].Value = "some text";
DataGridViewCheckBoxCell CheckBoxCell = new DataGridViewCheckBoxCell();
CheckBoxCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.dataGridView1[0, 2] = CheckBoxCell;
this.dataGridView1[0, 2].Value = true;
DataGridViewTextBoxCell txt = new DataGridViewTextBoxCell();
txt.ToolTipText = "Enter Ur Name";
this.dataGridView1[0, 3] = txt;
this.dataGridView1[0, 3].Value = "lakshman";
/*
* Second method : Add control to the host in the cell
*/
DateTimePicker dtp = new DateTimePicker();
dtp.Value = DateTime.Now.AddDays(-10);
//add DateTimePicker into the control collection of the DataGridView
this.dataGridView1.Controls.Add(dtp);
//set its location and size to fit the cell
dtp.Location = this.dataGridView1.GetCellDisplayRectangle(0, 3, true).Location;
dtp.Size = this.dataGridView1.GetCellDisplayRectangle(0, 3, true).Size;
TextBox Text = new TextBox();
Text.Text = "This my Name";
Text.BackColor = Color.RosyBrown;
this.dataGridView1.Controls.Add(Text);
Text.Location = this.dataGridView1.GetCellDisplayRectangle(0, 4, true).Location;
Text.Size = this.dataGridView1.GetCellDisplayRectangle(0, 4, true).Size;
}

Related

Changing double clicking activation on Combo box cell to single click?

I have a setup in my code where there is a datagridview. For each row I have a combo box cell that I have a separate combo box cell since I want a different selection of items for each cell.
Problem : The cell only drops down when the arrow is double clicked. How can I change the cell formatting, or possibly a cell click event, so that the cell response to just one click?
Here's my cell creation code. Frankly, I didn't start any other code since I didn't know what event to touch or call. Is there a property I can edit?
Code:
'add items to combobox list
Dim comboCell As New DataGridViewComboBoxCell
comboCell.FlatStyle = FlatStyle.Flat
Dim resolutionList As New List(Of cmbStruct)
Dim currentResIndex As Integer = 0
'create list of resolutions
For j As Integer = 0 To resolutions.Length - 1
Dim resClass As New cmbStruct
resClass.Name = resolutions(j)
resClass.ID = resolutions(j)
resolutionList.Add(resClass)
comboCell.Items.Add(resolutions(j))
Next
'set combocell values
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
'set the default value to the current resolution index
Try
comboCell.Value = resolutions(currentResIndex)
Catch ex As Exception
End Try
comboCell.ValueType = GetType(cmbStruct)
comboCell.DataSource = resolutionList
editCameraTable("Resolution", i) = comboCell
Next
Change the EditMode property:
DataGridView1.EditMode = DataGridViewEditMode.EditOnEnter
There seems to be a nearly identical question and a very good answer. It involves using the click_event. Here is the link:
How to manually drop down a DataGridViewComboBoxColumn?
In the link:
Private Sub cell_Click(ByVal sender As System.Object, ByVal e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
DataGridView1.BeginEdit(True)
If DataGridView1.Rows(e.RowIndex).Cells(ddl.Name).Selected = True Then
DirectCast(DataGridView1.EditingControl, DataGridViewComboBoxEditingControl).DroppedDown = True
End If
End Sub

Clear the DataGridView without loosing the source

I have a form with DataGridView, dataSourced by dataTable. When i insert the record at the end i press the Clear button or form all fields (textboxes, comboboxes, DGV checkboxes) get cleared automatically when i press the save button.
I normally clear the textboxes and comboboxes by writing:
textbox1.text = ""
combobox1.text = ""
but when i do clear DGV with this like:
dgvPurchase.dataSource = nothing
dgvPurchase.Refresh()
or
dgvPurchase.rows.clear()
It does clear the DGV but when i add new entry so DataGridView and data both do not appear into DGV. On the Form Load event I have the following code:
dtField = retrieveFull("ProductBasicInfo")
cmbPurProdID.DisplayMember = "ProdName"
cmbPurProdID.ValueMember = "ProdID"
cmbPurProdID.DataSource = dtField
dtPurchase.Columns.Add("ProdID")
dtPurchase.Columns.Add("ProdName")
dtPurchase.Columns.Add("RetailerID")
dtPurchase.Columns.Add("RetailerName")
dtPurchase.Columns.Add("UnitPrice")
dtPurchase.Columns.Add("Qty")
dtPurchase.Columns.Add("Amount")
dgvPurchase.DataSource = dtPurchase
Please guide me that is there any way that my DataSource do not disturb or if I clear it so it also refresh the DataSource.
This is the add button code, which adds the data into DataGridView
Try
For a As Integer = 0 To dtPurchase.Rows.Count - 1
If dtPurchase.Rows(a).Item("ProdID") = cmbPurProdID.SelectedValue Then
MessageBox.Show("This product is already enlisted")
Exit Sub
End If
Next
dRow = dtPurchase.NewRow
dRow.Item("ProdID") = cmbPurProdID.SelectedValue
dRow.Item("ProdName") = cmbPurProdID.Text
dRow.Item("RetailerID") = cmbPurRetailerID.SelectedValue
dRow.Item("RetailerName") = cmbPurRetailerID.Text
dRow.Item("UnitPrice") = txtPurUnitPrice.Text.Trim
dRow.Item("Qty") = txtPurQty.Text.Trim
dRow.Item("Amount") = txtPurAmount.Text.Trim
dtPurchase.Rows.Add(dRow)
You can create new empty DataTable and set it like this:
c#
DataTable emptyDT = new DataTable();
dgvPurchase.DataSource = emptyDT
vb.Net
Dim emptyDT As New DataTable()
dgvPurchase.DataSource = emptyDT
Does this work for you?

List view sort arrow vb.net

i am trying to draw sort arrows on list view column header along with the default visual styles
so far i have got this
Private Sub List_DrawColumnHeader(sender As Object, e As DrawListViewColumnHeaderEventArgs) Handles List.DrawColumnHeader
e.DrawDefault = True
If e.ColumnIndex = selectedIndex Then
e.Graphics.DrawImage(ImageList1.Images(1), CType(e.Bounds.Left + e.Bounds.Width / 2, Single) - 5, -2)
End If
End Sub
but the visual style is drawn over the arrow somehow
so i figured i could try this :
Private Sub List_DrawColumnHeader(sender As Object, e As DrawListViewColumnHeaderEventArgs) Handles List.DrawColumnHeader
e.DrawDefault = True
If lastDrawn.ColumnIndex = selectedIndex Then
e.Graphics.DrawImage(ImageList1.Images(1), CType(lastDrawn.Bounds.Left + lastDrawn.Bounds.Width / 2, Single) - 5, -2)
End If
lastDrawn=e
End Sub
and it draws the arrow when the next corresponding column is being drawn
but with this i cant get it to draw for the last column
Screenshots:
In order to use the .NET build in solution for showing a custom icon for a list view column header you need to:
create an ImageList
add three images (up / down arrow and empty) to it
bind the image list to the ListView control
bind to the ColumnClick event of the ListView control
when sorting the columns set the ImageKey property of the currently sorted column depending on the sorting direction
This example class (a simple form) shows how to set the images correctly not using custom drawing for the ListView header columns.
It does not implement any sort! (how to implement a ListViewSorter is shown in this MSDN article)
You need to implement a custom ListView-Sorter class and retrieve the image or the image key from it, after a column is sorted.
Public Class SimpleForm
Inherits Form
Private sortItems = New ImageList()
Dim lv As ListView = New ListView()
Dim so = System.Windows.Forms.SortOrder.Ascending
Public Sub New()
' create columns, items and ListView
Dim columns = New List(Of ColumnHeader)
Dim c1 = New ColumnHeader()
c1.Name = "c1"
c1.Text = "Name"
Dim c2 = New ColumnHeader()
c2.Name = "c2"
c2.Text = "Type"
columns.Add(c1)
columns.Add(c2)
Dim items = New List(Of ListViewItem)
Dim i1 = New ListViewItem("Terminator")
i1.SubItems.Add("T1000")
Dim i2 = New ListViewItem("Terminator")
i2.SubItems.Add("T10")
Dim i3 = New ListViewItem("J.C.")
i3.SubItems.Add("Human")
items.Add(i1)
items.Add(i2)
items.Add(i3)
' init and bind column click
lv.Columns.AddRange(columns.ToArray())
lv.Items.AddRange(items.ToArray())
lv.SmallImageList = sortItems
lv.View = View.Details
lv.Dock = DockStyle.Fill
Controls.Add(lv)
AddHandler lv.ColumnClick, AddressOf clickEventHandler
' init images list
sortItems.TransparentColor = System.Drawing.Color.Transparent
sortItems.Images.Add("up", Image.FromFile("d:\temp\32\arrow_up.gif"))
sortItems.Images.Add("down", Image.FromFile("d:\temp\32\arrow_down.gif"))
sortItems.Images.Add("empty", Image.FromFile("d:\temp\32\check.gif"))
End Sub
Private Sub clickEventHandler(ByVal o As Object, ByVal e As ColumnClickEventArgs)
' Implement a custom ListViewItemSorter and fetch the icon from it!
' Set the ListViewItemSorter property to a new ListViewItemComparer
' object. Setting this property immediately sorts the
' ListView using the ListViewItemComparer object.
' THIS CODE SHOWS ONLY HOW TO SET THE SORT ICON!
For i As Integer = 0 To lv.Columns.Count - 1
If (i = e.Column) Then
Select Case (so)
Case System.Windows.Forms.SortOrder.Ascending
lv.Columns(i).ImageKey = "up"
so = System.Windows.Forms.SortOrder.Descending
Case System.Windows.Forms.SortOrder.Descending
lv.Columns(i).ImageKey = "down"
so = System.Windows.Forms.SortOrder.Ascending
Case Else
lv.Columns(i).ImageKey = "empty"
so = System.Windows.Forms.SortOrder.None
End Select
Else
lv.Columns(i).ImageKey = "empty"
End If
Next i
End Sub
End Class
The output looks like this:

Force datagridview call row validating after finish input

I'm creating a Form with a Bound Datagridview inside it. On Form_Load or Row_Validating, I added new row to Datagridview by:
Private Sub PurchaseInRowAdd()
'add new row to datagridview
Dim dtRow As DataRow = CType(Me.dgvPurchaseIn.DataSource, DataTable).NewRow()
dtRow.Item("InvoiceID") = 0
dtRow.Item("KindID") = CType(CType(Me.dgvPurchaseIn.Columns("colPurchaseKind"), DataGridViewComboBoxColumn).DataSource, DataTable).Rows(0)("KindID")
dtRow.Item("InvoiceSign") = ""
dtRow.Item("InvoiceNo") = ""
dtRow.Item("InvoiceDate") = New Date(objController.ProcessYear, objController.ProcessMonth, 1)
dtRow.Item("ID") = CType(CType(Me.dgvPurchaseIn.Columns("colPurchaseCustomer"), DataGridViewComboBoxColumn).DataSource, DataTable).Rows(0)("ID")
dtRow.Item("Product") = ""
dtRow.Item("Price") = "0.00"
dtRow.Item("Note") = ""
dtRow.Item("Tax") = CType(CType(Me.dgvPurchaseIn.Columns("colPurchaseKind"), DataGridViewComboBoxColumn).DataSource, DataTable).Rows(0)("Tax")
dtRow.Item("TaxCode") = CType(CType(Me.dgvPurchaseIn.Columns("colPurchaseCustomer"), DataGridViewComboBoxColumn).DataSource, DataTable).Rows(0)("TaxCode")
dtRow.Item("VAT") = ""
CType(Me.dgvPurchaseIn.DataSource, DataTable).Rows.Add(dtRow)
End Sub
The problem here is, when user finished input in that new row and press enter, Row_Validating hasn't been fired because there's no row below it. So how can I force Row_Validating trigger when user finished input and press enter?
I have found this solution, but it doesn't suit my case because I don't want to set Enable Adding to True. I want to handle row adding by code instead.
I found the solution, I subclassed DataGridView and override ProcessDialogKey like this:
protected override bool ProcessDialogKey(Keys keyData)
{
if(keyData == Keys.Enter)
{
KeyEnterPress(this, new EventArgs());
}
return base.ProcessDialogKey(keyData);
}
(I wrote this subclass in C#)
Then handle key enter press in my form like this
Private Sub PurchaseInCellKeyDown(ByVal sender As Object, ByVal e As EventArgs)
If Me.dgvPurchaseIn.CurrentRow.Index = Me.dgvPurchaseIn.Rows.Count - 1 Then
If PurchaseInRowValidate(Me.dgvPurchaseIn.CurrentRow.Index, True) Then
Me.PurchaseInRowAdd()
Me.deselectPurchaseCell(Me.dgvPurchaseIn.CurrentRow.Index)
Me.dgvPurchaseIn.Rows(Me.dgvPurchaseIn.Rows.Count - 1).Cells("colPurchaseSign").Selected = True
End If
End If
End Sub
This line:
Me.dgvPurchaseIn.Rows(Me.dgvPurchaseIn.Rows.Count - 1).Cells("colPurchaseSign").Selected = True
will trigger row validating

Controls are not added to tabpage VB.NET

I run the following code in the constructor of a window. The "label" gets added but none of the other controls are shown on screen. If I debug the newTab.Controls there are several controls in it. Why don't they show up on the screen and I only see the "label" control.
Thanks
Dim graphlist As ArrayList = New ArrayList
For Each funct As TL_FUNCTION In functionlist
If (funct.functionname = functi) Then
If Not (graphlist.Contains(funct.picture)) Then
graphlist.Add(funct.picture)
End If
End If
Next
For Each picture In graphlist
Dim NewTab As New TabPage
NewTab.Name = picture
NewTab.Text = NewTab.Name
Me.TabControl1.Controls.Add(NewTab)
Me.TabControl1.SelectedIndex = Me.TabControl1.TabCount - 1
For Each func As TL_FUNCTION In functionlist
If (func.picture = picture) Then
Dim label As Label = New Label
label.Text = func.curve.ToString
NewTab.Controls.Add(label) 'This label shows up
Dim key As String
Dim values() As String
For Each key In func.values.Keys
values = func.values.GetValues(key)
For Each value As String In values
Dim label2 As New Label
label2.Text = key.ToString
Dim textb As TextBox = New TextBox
textb.Text = value
NewTab.Controls.Add(label2) 'this one is not shown on the tab
NewTab.Controls.Add(textb) 'this one is not shown on the tab
Next value
Next key
End If
Next
Next
You are placing the new labels and textboxes underneath the new label that you see in the TabPage because you never set their location, so it defaults to point (0, 0).
Try setting the location for the controls:
For Each value As String In values
Dim label2 As New Label
label2.Text = key.ToString
label2.Location = New Point(10, NewTab.Controls.Count * 24)
Dim textb As TextBox = New TextBox
textb.Text = value
textb.Location = New Point(label2.Right + 4, label2.Top)
NewTab.Controls.Add(label2)
NewTab.Controls.Add(textb)
Next value