I am going to attempt to add a click event to peep at the password. I was wondering if a button could be added to the right of each textbox in the column to do that with.
I have created a password text box with a button like that for use in a login form, but I don't know how (if possible) to add a button to a class like this.
Last edited by Shaggy Hiker; Apr 16th, 2018 at 09:53 AM.
This is great. Works perfectly (after changing .value to .value.tostring.
I am going to attempt to add a click event to peep at the password. I was wondering if a button could be added to the right of each textbox in the column to do that with.
I have created a password text box with a button like that for use in a login form, but I don't know how (if possible) to add a button to a class like this.
If you're talking about adding buttons to a DataGridView then you simply add a column to the grid and select Button as the type, or create a DataGridViewButtonColumn in code. You can then handle the CellContentClick event of the grid to handle clicks in that column. Note that you will need to be setting the PasswordChar property of the cell in the same row rather than of the column.
I actually did just that. Sadly, there's no image property to show an eye.icon. Oh, well. Anyway, here is what I have so far:
Code:
Private Sub Main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim btn As New DataGridViewButtonColumn()
TableDataGridView.Columns.Insert(5, btn)
btn.HeaderText = Nothing
btn.Text = "Peek"
btn.Width = 20
btn.Name = "btn"
Code:
Private Sub DataGridView_CellMouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles MyGrid.CellMouseDown
If e.ColumnIndex = 5 Then
Dim Box As New DataGridViewPasswordTextBoxCell
If MyGrid.RowCount > 0 Then
Dim MyDesiredIndex As Integer = MyGrid.CurrentRow.Index
MyGrid.ClearSelection()
MyGrid.CurrentCell = MyGrid.Rows(MyDesiredIndex).Cells(4)
'MyGrid.Rows(MyDesiredIndex).Selected = True
Box = CType(MyGrid.CurrentCell, DataGridViewPasswordTextBoxCell)
Box.UseSystemPasswordChar = False
End If
End If
End Sub
Private Sub DataGridView_CellMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles MyGrid.CellMouseUp
If e.ColumnIndex = 5 Then
Dim Box As New DataGridViewPasswordTextBoxCell
If MyGrid.RowCount > 0 Then
Dim MyDesiredIndex As Integer = MyGrid.CurrentRow.Index
MyGrid.ClearSelection()
MyGrid.CurrentCell = MyGrid.Rows(MyDesiredIndex).Cells(4)
'MyGrid.Rows(MyDesiredIndex).Selected = True
Box = CType(MyGrid.CurrentCell, DataGridViewPasswordTextBoxCell)
Box.UseSystemPasswordChar = True
End If
End If
End Sub
The Mouseup event isn't working, but the mousedown is. ???
Why are you creating a new DataGridViewPasswordTextBoxCell and then discarding it and using one that already exists? Don't create new objects if you have no intention of using them.
I'm not sure what you mean, Jim. I am using 'box' after creating it.
Anyway, this modified code is working perfectly.
Code:
Private Sub DataGridView_CellMouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles MyGrid.CellMouseDown
If e.ColumnIndex = 5 And e.RowIndex > -1 Then
MyGrid.ClearSelection()
Dim Box As New DataGridViewPasswordTextBoxCell
If MyGrid.RowCount > 0 Then
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(4)
Box = CType(MyGrid.CurrentCell, DataGridViewPasswordTextBoxCell)
Box.UseSystemPasswordChar = False
MyGrid.ClearSelection()
Box.Selected = False
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(5)
End If
End If
End Sub
Private Sub DataGridView_CellMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles MyGrid.CellMouseUp
If e.ColumnIndex = 5 And e.RowIndex > -1 Then
MyGrid.ClearSelection()
Dim Box As New DataGridViewPasswordTextBoxCell
If MyGrid.RowCount > 0 Then
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(4)
Box = CType(MyGrid.CurrentCell, DataGridViewPasswordTextBoxCell)
Box.UseSystemPasswordChar = True
MyGrid.ClearSelection()
Box.Selected = False
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(5)
End If
End If
End Sub
I am creating a new instance of the Thing class. If I then do this:
vb.net Code:
t = GetAThingFromSomewhereElse()
I am throwing away the Thing object I just created and getting an existing Thing object from elsewhere. Why would I create a new Thing if I wanted to use a Thing that already exists? What do you think the New keyword does? It creates a new object. Don't create a new object if you want to use an existing object. The New keyword is NOT part of a variable declaration. This is a declaration:
vb.net Code:
Dim t As Thing
This:
vb.net Code:
Dim t As New Thing
is shorthand for this:
vb.net Code:
Dim t As Thing = New Thing
i.e. it declares a variable, creates a new object and assigns that object to the variable. There's no point creating a new object and assigning it to the variable if you're going to discard it when you assign an existing object to the variable right after. Again, do not create a new object if you don't want to use a new object.
Note that that doesn't have anything to do with your issue. It's just proper programming. I haven't had a chance to have a closer look at the issue but I've never seen an example of handling anything but CellContentClick on a button cell before so I'm not sure what the implications are.
Thanks for getting the thread moved, Jim.
I thought my code was working correctly, but it has a glitch. When doubleclicking a cell to copy the content, it starts setting every cell I enter to usesystemdefaultpasswordchar while typing in it. When another cell is selected, then it goes back to normal. ...hard to explain. I have verified that the doubleclick sub is not resposible because I get the same glitch with it commented out. I don't see how it can make any cell outside of the DataGridViewPasswordTextBoxColumn usesystemdefaultpasswordchar at all, much less how my code is doing it.
That suggests that there's no issue handling the appropriate events and setting the appropriate property. That said, I didn't see any change in the grid when doing that. What that suggests is that the DataGridViewPasswordTextBoxCell does not refresh itself when that property changes. I did some further testing and found that, if I set the UseSystemPasswordChar property to False but not back to True, the affected cell does refresh correctly the next time I enter it. The upshot is that the DataGridViewPasswordTextBoxCell needs a bit of a tweak to fix that behaviour. I probably never tested that scenario when I first wrote that class because I didn't expect those password-related properties to be changing like that. I'll take a closer look at the code for the class and see if I can rectify that.
While I doubt that it would make any difference, try using DirectCast instead of CType. In fact, always use DirectCast to perform a cast and only use CType when you specifically need to, which may be never.
As I have demonstrated, what you want to do works as expected. You must be doing something wrong but it's not immediately obvious what that is from simply reading the code. That's why you don't simply read code to detect issues. You debug and test. Start by debugging your existing code. Set a breakpoint and step through it, line by line. At each step, test the application state to confirm that it is what you expect. As soon as it's not, you've found an issue and can investigate that specifically. Testing the state means evaluating any variables, methods and/or other expressions to make sure that you're working with the data you think you are.
If you still can't work out what's wrong, create a new test project to isolate just this functionality. Start with the code I provided, which I know works. If it doesn't work for you then there's an issue with your system. If it does, start to change it, one step at a time, to make it more like the code you think you want. As soon as the app behaviour deviates from what you expect, you've found an issue. That is what software development is, as much as it is writing new code to implement new functionality.
Believe me, I have been trying just that (although vb2015 doesn't do step into the way older versions did.
I don't remember if I mentioned it before, but it only happens after double_clicking in a cell in the password column, and not every time. But once it starts, it is persistant until re-opening the application.
When clicking on any cell, the program runs through this sub in DataGridViewPasswordTextBoxCell.vb:
Code:
Public Overrides Sub DetachEditingControl()
MyBase.DetachEditingControl()
With DirectCast(Me.DataGridView.EditingControl, TextBox)
'Reset the old password properties of the editing control.
.PasswordChar = Me.editingControlPasswordChar
.UseSystemPasswordChar = Me.editingControlUseSystemPasswordChar
End With
End Sub
Could that be the issue? It seems odd that a mousedown on any cell other than the password column should trigger this.
It seems odd that a mousedown on any cell other than the password column should trigger this.
It's not odd at all. It's exactly what you should expect.
There are no controls in a DataGridView by default. That's necessary for performance. Try adding a lot of child controls to a TableLayoutPanel and then doing things like scrolling and resizing and you'll understand why. Everything you see in the grid is just a rendering. When you start an editing session in a cell, a child control is embedded in that cell. That's why the grid stops raising keyboard events while in editing mode: keyboard input is going to the editing control. When you end the editing session, the editing control is removed from the cell. That can happen in a few different ways but navigating to a different cell is probably the most common.
When an editing session is begun, an editing control will be reused if one of the same type was used for the last editing session, otherwise a new control is created. That method that you posted is what gets executed in a DataGridViewPasswordTextBoxCell when an editing session ends. It resets the password-related properties of the editing control (which will be a TextBox) so that it doesn't continue to mask characters if it gets reused in a cell in another column. That code should be specifically preventing what you seem to be suggesting that you're seeing.
Try creating a new project with the minimum functionality necessary to reproduce this issue. You can then ZIP the solution (after deleting bin and obj folders) and attach it to a post and I'll take a closer look and see if I can see what's going on. It's certainly possible that there's an issue with those classes I wrote. I never really used them myself so they weren't exactly tested rigorously.
By the way, the size of your ZIP files suggests that you did not delete the bin and obj folders, which I specifically said that you need to do. Those folders contain binaries and this site doesn't allow attaching binaries.
I just double checked the zip file. The folders in question were deleted. The largest files in it is the MDF file @ 3,264kb and Eye.png @ 20,552kb.
Anyway, I believe I fixed the issue by changing the mouseup event. Some of the addition my be worthless, but it works.
Code:
Private Sub DataGridView_CellMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles MyGrid.CellMouseUp
If e.ColumnIndex = 5 And e.RowIndex > -1 Then
MyGrid.ClearSelection()
Dim Box As DataGridViewPasswordTextBoxCell
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(4)
Box = DirectCast(MyGrid.CurrentCell, DataGridViewPasswordTextBoxCell)
Box.UseSystemPasswordChar = True
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(5)
ElseIf e.ColumnIndex <> 5 And e.ColumnIndex <> 4 And e.RowIndex > -1 Then
Dim Box As DataGridViewCell
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(e.ColumnIndex)
Box = MyGrid.CurrentCell
MyGrid.Invalidate()
End If
End Sub
Last edited by Amerigoware; Apr 18th, 2018 at 10:55 AM.
I just double checked the zip file. The folders in question were deleted. The largest files in it is the MDF file @ 3,264kb and Eye.png @ 20,552kb.
That would explain it but I did say this:
Try creating a new project with the minimum functionality necessary to reproduce this issue.
A 3MB database and a 20MB image don't really qualify as minimum.
Originally Posted by Amerigoware
Code:
Private Sub DataGridView_CellMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles MyGrid.CellMouseUp
If e.ColumnIndex = 5 And e.RowIndex > -1 Then
MyGrid.ClearSelection()
Dim Box As DataGridViewPasswordTextBoxCell
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(4)
Box = DirectCast(MyGrid.CurrentCell, DataGridViewPasswordTextBoxCell)
Box.UseSystemPasswordChar = True
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(5)
ElseIf e.ColumnIndex <> 5 And e.ColumnIndex <> 4 And e.RowIndex > -1 Then
Dim Box As DataGridViewCell
MyGrid.CurrentCell = MyGrid.Rows(e.RowIndex).Cells(e.ColumnIndex)
Box = MyGrid.CurrentCell
MyGrid.Invalidate()
End If
End Sub
I don't really understand why you're changing the CurrentCell. Maybe that had something to do with your issue. As for the code, I'd say that the first and third lines of the Else block are useless at least. It also seems rather excessive to invalidate the entire grid. It also seems rather pointless to set the CurrentCell twice in the If block.