Force datagridview call row validating after finish input - vb.net

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

Related

Iterate through timer then get their names

Yes, by spending time on research I am aware that
A Timer is a Component not a Control so it will not be located in the
Control Collection. This is a case where it is probably better to not
use a common button click handler since it is not simplifying
anything. (Source)
But, is there anything that I can do to pass my current situation?
What I want is whenever a user clicks a button (I have many buttons) it will determine if the status of that button is Start or Stop then if it is stop it should start.
Stop and Start correspond to the timer, the number of buttons I have corresponds to the number of timers I have.
I can easily linked them by creating a function like this
Function isBTNStatusEnabled(sender As Object) As Boolean
Dim result As Boolean = False
Dim btnStatus As Button = DirectCast(sender, Button)
Dim btnStatusNumber As String = btnStatus.Name.Substring(btnStatus.Name.Length - 1)
Console.WriteLine("Found the " & btnStatus.Name)
If btnStatus.Text = "Start" Then
btnStatus.Text = "Stop"
result = True
btnStatus.BackColor = Color.Red
Else
btnStatus.Text = "Start"
result = False
btnStatus.BackColor = SystemColors.Control
End If
For Each frmTesterObjects As Object In Me.components.Components
If TypeOf frmTesterObjects Is Timer And DirectCast(frmTesterObjects, Timer).Tag.ToString = "tmrString" & btnStatusNumber Then
'what to do
Console.WriteLine("Timer name: " & DirectCast(frmTesterObjects, Timer).Tag.ToString)
End If
Next frmTesterObjects
Return result
End Function
My problem is this part of code
If TypeOf frmTesterObjects Is Timer And DirectCast(frmTesterObjects, Timer).Tag.ToString = "tmrString" & btnStatusNumber Then
Console.WriteLine("Timer name: " & DirectCast(frmTesterObjects, Timer).Tag.ToString)
End If
I am trying to get the name of the timer by iterating through all objects in my forms. I can easily group all of the objects by properly naming them, for example in set 1 I have btnStatus1 and tmrString1 then in set 2 I have btnStatus2 and tmrString2, only the last string which is a number changes.
I've almost always found that searching for components by name at run-time will eventually fail if you continue to maintain/modify the form. It is far better, in my opinion, to make sure you have a compile-time check.
I would do this code instead:
Function isBTNStatusEnabled(sender As Object) As Boolean
Dim button2Timer = New Dictionary(Of Button, Timer) From
{{Button1, Timer1}, {Button2, Timer2}, {Button3, Timer3}}
Dim result As Boolean = False
Dim btnStatus As Button = DirectCast(sender, Button)
Console.WriteLine("Found the " & btnStatus.Name)
If btnStatus.Text = "Start" Then
btnStatus.Text = "Stop"
result = True
btnStatus.BackColor = Color.Red
Else
btnStatus.Text = "Start"
result = False
btnStatus.BackColor = SystemColors.Control
End If
Console.WriteLine("Timer name: " & button2Timer(btnStatus).Tag.ToString())
Return result
End Function
The Dictionary(Of Button, Timer) hard-codes the mapping in so that there is no need to search for the Timer. This also alleviates the need to actually name and tag the buttons and timers.
Just for the fun of it I have had a go at implementing your full solution based on your answer to my comment on the question. Here it is:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim parts =
{
New With {.Button = Button1, .NumericUpDown = NumericUpDown1, .TextBox = TextBox1, .Port = 1},
New With {.Button = Button2, .NumericUpDown = NumericUpDown2, .TextBox = TextBox2, .Port = 2},
New With {.Button = Button3, .NumericUpDown = NumericUpDown3, .TextBox = TextBox3, .Port = 3}
}
Dim query =
From p In parts
Select
Observable _
.FromEventPattern(Sub(h) AddHandler p.Button.Click, h, Sub(h) RemoveHandler p.Button.Click, h) _
.ObserveOn(Me) _
.Do(Sub(ep)
Dim start = p.Button.Text = "Start"
p.Button.Text = If(start, "Stop", "Start")
p.Button.BackColor = If(start, Color.Red, SystemColors.Control)
p.NumericUpDown.Enabled = Not start
p.TextBox.Enabled = Not start
End Sub) _
.Select(Function(ep) _
Observable _
.Interval(TimeSpan.FromSeconds(p.NumericUpDown.Value)) _
.Select(Function(n) New With {p.TextBox.Text, p.Port}) _
.TakeWhile(Function(x) p.Button.Text = "Stop")) _
.Switch()
query _
.Merge() _
.ObserveOn(Me) _
.Subscribe(
Sub(x)
TextBox4.Text = TextBox4.Text + String.Format("Text ""{0}"" on Port ""{1}""{2}", x.Text, x.Port, Environment.NewLine)
End Sub)
End Sub
I've used Microsoft's Reactive Framework for all of the event handling and timers. You just need to NuGet "Rx-WinForms" into your app to use it.
You'll see that parts contains a list of the button, numeric up/down, text box and a port number.
query takes these parts and creates handlers for the button clicks. Based on the text within the button it starts timers based on the numeric up/down controls and text boxes. Query simply produces a stream of values in the form of { .Text = "Foo", .Port = 1 }. query also uses a .Do(...) operator to update the UI while the timers are running.
Finally the .Subscribe(...) code takes these values, and, in my app, adds them to a text box on the form so that I can see all the output.
Here's an example of the form as I was using it:
I hope this is of interest.
I've just simplified your code. Here it is:
For Each x In Me.components.Components.OfType(Of Timer)()
If x.Tag = "tmrString" & btnStatusNumber Then
Console.WriteLine("Timer name: " & x.Tag)
End If
Next
All you have to do now is match the Tag of each Timer corresponding to its Name.

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

DataGridView not Refreshing/Updating/Reloading Data. After Child form closes

This is a VB.NET, Winforms App. I have a datagridview on "Form1" that uses a databinding.datasource which is an Entity Framework table. I fill the datagridview with the below function on Form1:
Sub PM_UnitViewGrid()
Try
_form1.UnitsBindingSource.DataSource = db.units.Where(Function(f) f.propertyId = _form1.CurrentPropertyId).OrderBy(Function(F) F.unitNumber)
_form1.UnitDataGridView.DataSource = _form1.UnitsBindingSource.DataSource
Dim iCount As Integer = _form1.UnitDataGridView.RowCount
For x As Integer = 0 To iCount - 1
If Not IsNothing(_form1.UnitDataGridView.Rows(x).Cells(4).Value) Then
Dim tid As Integer = _form1.UnitDataGridView.Rows(x).Cells(4).Value
Dim _ten As tenant = db.tenants.Single(Function(f) f.Occupantid = tid)
_form1.UnitDataGridView.Rows(x).Cells(1).Value = _ten.first_name + ", " + _ten.last_name
Else
Dim btnColumn As DataGridViewButtonCell = CType(_form1.UnitDataGridView.Rows(x).Cells(1), DataGridViewButtonCell)
btnColumn.Style.BackColor = Color.Green
_form1.UnitDataGridView.Rows(x).Cells(1).Value = "VACANT"
End If
Next
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
Return
End Sub
This works great and also assigns the needed values to an unbound column. The problem is that the cells(1) is a button. Which when clicked takes the user to another form as a new dialog window. The function for which is below. However, once the changes are made in that form I need for the datagridview to refresh the data that its using from the database and show the correct data. As it stands right now the values are not updating on the datagridview unless the app is completely exited and restarted. Nothing I have found seems to work and Refresh and Update only redraw the control. I need the underlying datasource to refresh and then the datagridview once the child form is exited.. This has had me stumped for a good 36 hours now and I am lost as to why nothing I am trying is working. ANY and all help would be greatly appreciated.
The sub that loads the child form based on the cells(1) button clicked is as follows:
Private Sub UnitDataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles UnitDataGridView.CellContentClick
UnitDataGridView.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)
Dim y As DataGridViewCellEventArgs = e
Dim Tid As Integer = Nothing
If e.ColumnIndex = 1 Then
If Not e.RowIndex = -1 Then
If Not IsNothing(UnitDataGridView.Rows(e.RowIndex).Cells(4).Value) Then
currentTenent = UnitDataGridView.Rows(e.RowIndex).Cells(4).Value
TenentIdentification = currentTenent
If Not IsNothing(e) Then
If Not IsNothing(UnitDataGridView.Rows(e.RowIndex).Cells(4).Value) Then
Tid = UnitDataGridView.Rows(e.RowIndex).Cells(4).Value
Dim _ten As tenant = db.tenants.Single(Function(f) f.Occupantid = Tid) 'tenant is a table entity
TenantViewSubs.tenId = _ten.Occupantid
Dim t As New TenantView
t.tenId = tid
t.ShowDialog()
End If
End If
PropertyManagSubs.PM_UnitViewGrid() 'This is the function that is above that fills the datagridview
Else
Dim uTview As New UnassignedTenants
uTview.selectedProperty = selectedProperty 'selectedProperty is Integer
uTview.ShowDialog()
PropertyManagSubs.PM_UnitViewGrid() 'This is the function that is above that fills the datagridview
End If
End If
End If
End Sub
I tried each of the following code blocks after the t.ShowDialog() line with no change at all.
UnitDataGridView.Refresh()
.
UnitsBindingSource.Dispose()
UnitsBindingSource.DataSource = db.units.Where(Function(f) f.propertyId = selectedProperty).OrderBy(Function(f) f.unitNumber)
UnitDataGridView.DataSource = UnitsBindingSource.DataSource
.
UnitsBindingSource.DataSource = nothing
unitsBindingSource.DataSource = db.units.Where(Function(f) f.propertyId = selectedProperty).OrderBy(Function(f) f.unitNumber)
UnitDataGridView.DataSource = UnitsBindingSource.DataSource
I finally fixed this on my own.. It was in the way I passed my db context to the databinding..
I simply wrote the below sub:
Private Sub UpdateValues()
Dim context As New storageEntities 'storageEntities is an Entity
Dim query = context.units.Where(Function(F) F.propertyId = selectedProperty).OrderBy(Function(f) f.unitNumber)
UnitDataGridView.DataSource = query
End Sub
Then anytime a child form updated data I simply call
UpdateValues()
After the dialog box closes.
This may help someone else with the same problems so that is why I am posting it.

Name a column in datagridview

I came up with following code that adds number of columns based on user input in textbox1, but how to add names to these columns? (Columns added should have names like, A1,A2,A3.......on the top most row)
Dim t As Integer
t = Val(TextBox1.Text)
For i = 1 To t
Form2.DataGridView1.ColumnCount = i
Next
Also can we freeze specific cells in a datagridview i.e. cells which user cannot edit?
Try this
DataGridView1.Columns(i).Name = String.Format("A{0}", i)
Once you have access to Columns(i) you can view available properties from the intellisense
DataGridView1.Columns(0).Frozen = True;
The DataGridView only has methods for freezing Rows or Columns, in order to block editing of a specific cell you can try adding a handler for the CellBeginEdit event then check for the Row and Column of the Cell(s) that you want to prevent editing of then cancel the event.
something like this:
Private Sub DataGridView1_CellBeginEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit
If e.ColumnIndex = 0 And e.RowIndex = 0 Then
e.Cancel = True
End If
End Sub
this.dataGridView1.Columns["StudentId"].ReadOnly = true;
from: http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/fef91d76-24c5-4b41-84d7-ba133de2d9a7#b2cb53ec-5b15-4385-b086-28a6dc93dfc9

Public subs don't work in this scenario

I have a form named FrmDrvouchers. It contains some public subprocedures.
I call these subs from another form which opens from a form which is opened from FrmDrVouchers as a dialog.
When I open FrmDrvouchers directly, everything works. But when I call FrmDrvouchers from another project which is also part of this solution, its public subs don't work when I call them from another (dialog) form.
Here is the code of the Button Click from which I open FrmDrvouchers:
Dim FrmDrv As FrmDrVouchers = New FrmDrVouchers()
FrmDrv.Show()
This works, but those public subs don't. Why?
Thanks For ur response,
It just skip what i want to do, but not throws any Exceptions,
Now I m posting my Code,,, plz have a look on that,,
Here is my Main form's MenuStrip Button Click code :
Private Sub CashPaymentToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CashPaymentToolStripMenuItem.Click
Dim DrVouchers As Transactions.FrmDebitVouchers = Transactions.New FrmDebitVouchers()
DrVouchers.Show()
End Sub
Here is the FrmDebitVouchers's Public Subs which are not working,,, in the sense that they skip the function which i have written but not throwing any Exception,,
Public Sub DrVoucherOPen(ByVal VoucherNo As Integer)
'Filling the Dataset with selected Voucher No to OPen the record
DebitVouchersTableAdapter.FillByVoucher(Flr12131DataSet.DebitVouchers, VoucherNo)
VoucherDateDateTimePicker.Enabled = False
End Sub
Public Sub DrVoucherBodyOPen(ByVal VoucherNo As Integer)
'---------Procedure to Open DrVouchersBody And to OPen it in Datagridview-------------'
'Getting the User No, and if it is admin then load fill dataset and allow user to edit the record
If GetUserNumber() = 1 Then
'Filling the dataset
DebitVouchersBodyTableAdapter.FillByVoucher(Flr12131DataSet.DebitVouchersBody, VoucherNo)
DrBodyDGV.DataSource = Nothing
Dim Sum As New Decimal
'Initializing the SerialNumbers variable
SerialNumbers = New List(Of Integer)
'Setting datagridview to opend record
For i = 0 To Flr12131DataSet.DebitVouchersBody.Rows.Count - 1
DrBodyDGV.Rows.Add()
DrBodyDGV.Rows(i).Cells(0).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("SerialNo")
DrBodyDGV.Rows(i).Cells(3).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("AccountNo")
DrBodyDGV.Rows(i).Cells(6).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("Debit")
DrBodyDGV.Rows(i).Cells(7).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("Narration")
'Getting serial No into List
SerialNumbers.Add(Flr12131DataSet.DebitVouchersBody.Rows(i).Item("SerialNo"))
'Getting Account Name into Datagridview
If Not Not IsNumeric(DrBodyDGV.Rows(i).Cells(3).Value) Then
Dim Qa As New Flr12131DataSetTableAdapters.QueriesTableAdapter
Dim StrAccountName = Qa.GetAccountName(DrBodyDGV.Rows(i).Cells(3).Value)
DrBodyDGV.Rows(i).Cells(5).Value = StrAccountName
Else
End If
Sum += DrBodyDGV.Rows(i).Cells(6).Value
Next
TxtTotal.Text = Sum
'Setting the controls properties for admin
DrBodyDGV.AllowUserToAddRows = True
DrBodyDGV.AllowUserToDeleteRows = True
DrBodyDGV.ReadOnly = False
BtnSave.Enabled = True
BtnDelete.Enabled = True
BtnPrint.Enabled = True
ToUpdate = True
Else
'If user is not admin then load all record and not allow user to modify it or delete
' Bounding the datagridview
Dim Sum As Decimal = 0
BtnSave.Enabled = False
DebitVouchersBodyTableAdapter.FillByVoucher(Flr12131DataSet.DebitVouchersBody, VoucherNo)
DrBodyDGV.DataSource = Flr12131DataSet.DebitVouchersBody
For i = 0 To DrBodyDGV.Rows.Count - 1
If Not Not IsNumeric(DrBodyDGV.Rows(i).Cells(3).Value) Then
Dim Qa As New Flr12131DataSetTableAdapters.QueriesTableAdapter
Dim StrAccountName = Qa.GetAccountName(DrBodyDGV.Rows(i).Cells(3).Value)
DrBodyDGV.Rows(i).Cells(5).Value = StrAccountName
Else
End If
Sum += DrBodyDGV.Rows(i).Cells(6).Value
Next
TxtTotal.Text = Sum
DrBodyDGV.AllowUserToAddRows = False
DrBodyDGV.AllowUserToDeleteRows = False
' DrBodyDGV.edit()
DrBodyDGV.ReadOnly = True
BtnSave.Enabled = False
BtnDelete.Enabled = False
BtnPrint.Enabled = True
End If
End Sub
Here is another the DrVouchersRecord form from which I call Public Subs:
Private Sub DrVouchersRecordDataGridView_CellDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DrVouchersRecordDataGridView.CellDoubleClick
Dim FrmDrVouchers As FrmDebitVouchers = New FrmDebitVouchers()
If FrmDrVouchers.DrBodyDGV.Rows.Count > 1 Then
Dim Ans As Integer
Ans = MsgBox("Unsaved changes will be lost, want to proceed", vbYesNo + vbInformation, "Alert")
If Ans = vbYes Then
Dim VoucherNo As New Integer
VoucherNo = DrVouchersRecordDataGridView.CurrentRow.Cells(0).Value
FrmDrVouchers.DrVoucherOPen(VoucherNo)
FrmDrVouchers.DrVoucherBodyOPen(VoucherNo)
Me.Close()
End If
Else
Dim VoucherNo As New Integer
VoucherNo = DrVouchersRecordDataGridView.CurrentRow.Cells(0).Value
FrmDrVouchers.DrVoucherOPen(VoucherNo)
FrmDrVouchers.DrVoucherBodyOPen(VoucherNo)
Me.Close()
End If
End Sub
My forms sequence is:
FrmMain is my main startUp form
After that FrmDebitVouchers Open on menustripButtonclick
After that DebitVouchersRecord is Open as Dialog from DrmDebitVouchers,,, and from these Public Subs are called
I Have added Refrence also
Waiting for ur answers,
This problem has hit many programmers coming from VB6 to VB.NET.
In your DataGridView_DoubleCellClick event you create a NEW instance of FrmDebitVouchers.
Then your code refers to property/methods/objects of this new INSTANCE, not of the original one created via CashPaymentToolStripMenuItem_Click. (Also note that this new instance is never showed on the screen, so you are sending/requesting changes to an hidden form instance)
Of course, the DrBodyDGV.Rows.Count is zero on this INSTANCE (referenced as FrmDrVouchers) because probably this INSTANCE has never been intialized like the first one.
Try to add a FrmDrVouchers.Show() after the creation and you will see the hidden SECOND INSTANCE of the form class FrmDebitVouchers.
To solve your problem, you need to pass the reference of the first FrmDebitVouchers instance to the DrVouchersRecord form (for example in the constructor or via a public property) and then use that reference instead of creating FrmDrVouchers