Canceling a TabControl tab selection in VB.NET - vb.net

Really wracking my brain here and I'm sure it's something simple I'm missing.
Basically I have a form with two tabs. I'm checking the controls on each tab to see if they're dirty and want to prevent a user from clicking a tab if there are changes on the current tab.
I had thought if I check the dirty variable and just set the tab index to the one that hasn't been selected I'd be fine but every time I programatically set a tab's property, it fires off a bunch of the tab's events that just produce an undesireable result such as the right control set is showing but the wrong tab is selected.
Here is the code for as close as I've gotten to getting it to work.
Private Sub objTabs_Selected(ByVal sender As Object, ByVal e As System.Windows.Forms.TabControlEventArgs) Handles objTabs.Selected
If bIsDirty Then
If Me.objTabs.SelectedIndex = 1 Then
Me.objTabs.SelectedTab = tabLetterofCreditBanks
Me.objTabs.SelectTab(0)
Else
Me.objTabs.SelectedTab = tabWireTransferBanks
Me.objTabs.SelectTab(1)
End If
End If
End Sub
Thanks in advance.

Try using the Selecting event instead of the Selected event - this will give you the opportunity to override the user's behavior (i.e. setting TabControlCancelEventArgs.Cancel to True).

Related

Show hourglass cursor application-wide during Load Event while ShowDialog

I have the following code:
Private Sub btnNew_Click(sender As Object, e As EventArgs) Handles btnNew.Click
Dim f As New frmStyle
f.ShowDialog()
End Sub
frmStyle does many things during it's Load event, so it doesn't appear immediately. Instead, it takes around 1 second to show up.
Because of that, I want to show an hourglass cursor during the form's Load event.
In VB6 it was super easy. I could just use "Screen.Cursor = vbHourglass".
Then you could set the cursor back to the default from whereever you wanted, for example at the end of a Form_Load event.
How can this be done in VB.NET now?
I want to show the cursor application-wide, and not for a single control only.
And ALSO (what makes my question unique and NOT answered so far in another question), I need to reset it, but it should be reset at the end of Form_Load (which is NOT the initializing element. Instead the button is the "initializing" element, but I can not set the cursor to default at the end of the button click because ShowDialog is shown modally. This means that the cursor would only be changed back if the form was closed again).
Thank you!

DataGridView resets to top when clicked after mouse scroll

I have a DataGridView control on a TabPage of a Windows Form application.
When the user moves the mouse over the DataGrid and uses the scroll wheel, the grid scrolls as expected. But when the user clicks in a cell on the screen, instead of the cell receiving focus, the DataGrid resets to the top and requires the user to scroll down again. This response is non-intuitive since it's not immediately obvious that the cell you thought you clicked on isn't there anymore.
I would be happy to prevent the DataGrid from responding to the scroll wheel until the user clicks in the grid, or preferably to maintain the current actions except not resetting to the top when first clicked.
From what I've researched here, it appears that the DataGrid is rebinding because I'm resetting the binding when the tabpage is entered (since the database might have been updated by one of the other tabs.
Private Sub TabPage1_Enter(sender As Object, e As EventArgs) Handles TabPage1.Enter
LoadTACTable()
End Sub
In LoadTACTable():
dbGetList("spSelectTACList", dtTACs, 0, 100000, Nothing) ' Record numbers are 0 based
bsTACs.DataSource = dtTACs
With gridTACs
' TOTAL Grid width = 1380
.DataSource() = bsTACs
.
.
.
(Showing only part of the code for brevity.
Is there a way to see if the TabPage is already displayed when entered? Or, is unnecessary to reset the gridTAC datasource every time I retrieve the data from the SQL database to the dtTACs datatable using my dbGetList() sub?
There are several possible solutions to your problem. One would be to not automatically rebind the datagrid but let the user do it by clicking some refresh button. That way the user would not see non-intuitive behavior.
You mentioned that the contents of one tab may need to be refreshed when the contents of other tabs are changed. Whenever the contents of a tab is changed and can affect other tabs, you could flag these other tabs (for example, by adding a star to their titles) to indicate that they no longer have the latest data. The user would then know that the tab needs to be refreshed.
There might be other solutions, but it is difficult to tell without knowing more about your use case.
With the guidance above, I believe I solved the issue:
I created a flag:
Dim TabDirty As Boolean
Then I set it in the TabPage.Leave handler:
Private Sub TabPage1_Leave(sender As Object, e As EventArgs) Handles TabPage1.Leave
dtTACs.Dispose()
TabDirty = True
End Sub
Then I just check it when I enter the TabPage:
Private Sub TabPage1_Enter(sender As Object, e As EventArgs) Handles TabPage1.Enter
If TabDirty = True Then
TabDirty = False
LoadTACTable()
End If
End Sub
So far, this appears to work - the grid is not getting reset when clicked, but I will do a bit more testing to confirm that the data is refreshed when necessary.

How to hide a DataGridViewButtonCell

I have a DataGridViewButtonCell in my DataGridView and I wanted to set the property Visible to True.
I have tried:
DataGridView1.Rows("number of row i want").Cells("number of cell i want").Visible = True
Unfortunately it says that the property visible is read only.
Here is the code:
Private Sub DataGridView1_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
'does not work
DataGridView1.Rows(e.RowIndex).Cells(6).Visible = True
End Sub
Does anyone knows how I can achieve this?
Thanks.
There is no actual way to hide a DataGridViewButtonCell. Currently I can only see two options:
Use padding to move the button over as shown here. I will provide similar VB.NET code
Set the Cell to a DataGridViewTextBoxCell and set the ReadOnly property to True
Use Padding:
Private Sub DataGridView1_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
If DataGridView1.Rows(e.RowIndex).Cells(6).GetType() Is GetType(DataGridViewButtonCell) Then
Dim columnWidth As Integer = DataGridView1.Columns(e.ColumnIndex).Width
Dim newDataGridViewCellStyle As New DataGridViewCellStyle With {.Padding = New Padding(columnWidth + 1, 0, 0, 0)}
DataGridView1.Rows(e.RowIndex).Cells(6).Style = newDataGridViewCellStyle
End If
End Sub
Use DataGridViewTextBoxCell:
Private Sub DataGridView1_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
If DataGridView1.Rows(e.RowIndex).Cells(6).GetType() Is GetType(DataGridViewButtonCell) Then
Dim newDataGridViewCell As New DataGridViewTextBoxCell
DataGridView1.Rows(e.RowIndex).Cells(6) = newDataGridViewCell
newDataGridViewCell.ReadOnly = True
End If
End Sub
Both of these should give you the effect of not showing the button.
This is really a perspective issue. From a programmer’s perspective, simply ignoring the button clicks on the buttons I want to disable is very easy to do and takes just a few lines of code.
From a user perspective, this situation would play out like this… the user clicks what appears to be a valid enabled button, and nothing happens. The user did not write the code for this… so at best the user will think the computer is not responding to the button click or at the worst… would think your coding skills are dubious!
The same situation happens if the button is missing. The user is not going to know why it is missing… but will most likely come to the same conclusion described above with a non-working button.
In another very simple approach, let say that all the buttons are enabled and we have a list of the button indexes we want to disable. The users presses one of the buttons, we check the disabled button list and if the clicked button is one that is disabled, simply display a message box to indicate why this button is disabled. This approach says to the user… “Here are a bunch of buttons, guess which ones are enabled”…
The DataGridViewDisableButtonCell and DataGridViewDisableButtonColumn wrappers solve all of the above issues… the button is visible so the user wont question where the button went if you set it to invisible and it is greyed out. “Greyed out” is something most users understand and will relieve the user of having to “guess” which buttons are enabled.
You can create a wrapper for two classes: the DataGridViewButtonCell and the DataGridViewButtonColumn.
The link How to: Disable Buttons in a Button Column in the Windows Forms DataGridView Control to the MS example is one I have used before using C#, however there is a VB implementation at the link also.
Below is a picture of the result of using the two wrappers described in the MS link. For testing, the picture below uses the check boxes to left of the button to disable the button on the right.
IMHO, using this strategy is user friendly. If you simply make the button invisible or read only, then the user is possibly going to think your code is messed up and not have a clear understanding of WHY the button is missing or doesn’t work. A disabled button indicates to the user that the button is not available for that item. An option would be to have a mouse roll-over indicating why the button is disabled.

Show Continuous Progress Bar on DataGridView Fill

I have two forms (Main.vb and Schedule.vb). Main has a toolstripmenu and a panel. When "View Schedule" is selected from the toolstripmenu, the Schedule form is opened within the Main form's panel. So it opens as a "sub form". This works perfectly.
Private Sub tsmiScheduleView_Click(sender As Object, e As EventArgs) Handles tsmiScheduleView.Click
Schedule.TopLevel = False
Schedule.Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right
Schedule.Size = New Size(pnlMain.Width, pnlMain.Height) 'anchors will be disrupted if form does not open to fit with main
Me.pnlMain.Controls.Add(Schedule)
Schedule.Show()
End Sub
The problem happens in Schedule.vb, there is a DataGridView there that gets populated via a TableAdapter. But it takes a long time to load. So I want to show a Continuous Progress Bar while the DataGridView is loading.
Private Sub Schedule_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Load main data into the Schedule DataGridView
Me.SchedulViewTable_TableAdapter.Fill(Me.DataSetSchedule.SchedulViewTable)
End Sub
I can get the progress bar to show easy enough, but it freezes when the DataGridView is loading.
I've tried using threads, backgroundworkers, putting the ProgressBar in a seperate form and displaying it from there, and I even nixed my ProgressBar idea for an animated gif using picture boxes. None of these are working for me. Can anyone help?
As a note, I would love to use this all over my program. Whenever I run into something that's going to take time I'd like to have an easy way to say: "Okay, display the Continuous Progress Bar on Main.vb until I'm done".
Thanks.
Set the DataGridView DataSource to null before executing the Fill command.
Then set the DataSource back to DataTable.
If you do that, how long does it take between the binding and the display (I would bet on 5 to 15 seconds)?
It seem like the only thing to do is load less rows into the DataGridView so there is no need for a progress bar. Not what I wanted to do but there isn't a way to do what I originally wanted.
I was able to show text, "Loading..." but no animation.

Make a button have multiple uses

okay... How do I explain this without being totally confusing?... Alright, I have this form that has MenuScripts (top-levels and second-levels). The problem that I am having is one of the second-levels is "Add" which brings you to another form when clicked. This other form has a button ("Record") and text boxes. This other form allows the user to input data and when the record button is clicked, the inputted data is written into a text file. Ok, so back to the first form. Another second-level MenuScript is "Update" which also brings the user to the other form; but first, the user has to click an item within a listbox to proceed. How do I get the data from the selected item to appear in the appropriate textboxes and how do I get the record button to update data instead of being confused and thinking it is only a add-data button?
Is there a way to use an "if" statement to say something like "if mnuAdd is clicked then" "elseif mnuUpdate is clicked then". Would something like that work for giving the record button multiple uses?
Also, if someone can give me some pointers on making sure the user selects an item within the listbox would definitely be a plus! Thanks, guys!
Unfortunately, I cannot add images since my reputation is too low.
Here is a visual representation of my ultimate goal
Easiest way: before displaying the second form set it's Tag property to something distinct – say "Add" or "Update" – depending on which menu item is selected. Then you just test the Tag value in the button's Click event and proceed accordingly.
As for determining whether a list item is selected: well if there isn't the ListBox's SelectedIndex property will be set to -1.
You need to put a public property on the second form (Details) which specifies which mode it is in. For instance, you could create a mode enumeration like this:
Public Enum EntryModes
AddBook
UpdateBook
End Enum
Then, define a public mode property on the second form, like this:
Public Property EntryMode As EntryModes
Get
Return _entryMode
End Get
Set(ByVal value As EntryMode)
_entryMode = value
End Set
End Property
Private _entryMode As EntryMode
Then, when you show the second form from the menu, just set the property first, before showing it:
Private Sub mnuAdd_Click(sender As Object, e As EventArgs)
Dim dialog As New DetailsDialog()
dialog.EntryMode = EntryModes.AddBook
dialog.ShowDialog()
End Sub
Private Sub mnuUpdate_Click(sender As Object, e As EventArgs)
Dim dialog As New DetailsDialog()
dialog.EntryMode = EntryModes.UpdateBook
dialog.BookToUpdate = ListBox1.SelectedItem
dialog.ShowDialog()
End Sub
As you can see, in the Upate menu click, I also added a line that passes the information for which book should be updated.