How can I refer to a control from within a control's method (like the "me" for classes)? - vb.net

How can I refer to the control while I am inside a control's method in VB.NET?
For example, I want in a textbox to show a message box with that textbox's text every time the text changes. The code would be something like:
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
msgbox("The text is:"+ Me.text)
' ok the line above wont work i already know that, because "Me" refer to the form,
' not the control textbox1
' how i will refer to the textbox1's text???
' i dont want to use "textbox1.text" is there a way similar like the "Me" is for forms?
' because i want to copy-paste a code like this in a lot of controls and do not want to
' have to change in every copy the name to each control name
End Sub
I hope I made myself clear; my English needs some improvement :D

No, there's no keyword that allows you to do that. However, every event raised by a control passes in a sender parameter that you can use to determine which particular control raised that event.
Note that this parameter is always typed as a basic Object (because it can represent any possible control), so you'll need to downcast to a more specific control class if you need to access any of the unique members that it exposes. Since you're handling an event raised by a TextBox control, you know that the sender must be of type TextBox, so you can simply use DirectCast to handle the upcasting. You don't have to worry that an InvalidCastException will be thrown.
For instance, your above example would become:
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Handles TextBox1.TextChanged
Dim textBox As TextBox = DirectCast(sender, TextBox)
MessageBox.Show("The text is: " & textBox.Text)
End Sub
That being said, there are a couple of concerning things that jump out at me in your question:
Any time that your approach to solving a problem is "copy-pasting" code, you should stop, take a step back, and try to figure out if there's any better way to achieve your ultimate goal.
For example, if you need every textbox on your form to react in the same way whenever a particular event is raised, you should consider subclassing the existing TextBox control and consolidating all of your code in one place. Remember that you can inherit off of most of the standard controls to add custom functionality. This is often a far better solution than copying and pasting code to multiple places in your project. If you ever need to track down a bug or modify that functionality, you'll only have to change it one place in your code, rather than several. As a somewhat cheekier benefit, you'll be able to use Me to refer to that control when you're editing its subclass.
You should always prefer to concatenate (combine) strings using the & operator in VB.NET, rather than the + sign. Or perhaps even better, the String.Concat or String.Format methods.
There is no reason to use MsgBox in VB.NET, as opposed to MessageBox.Show. No, this won't improve performance of your application, but it's a good practice to get into for .NET languages.

The sender variable contains the TextBox instance you want to access. You only need to convert the sender to TextBox.

Related

VB.NET Reference the user form in which the sender control is located

I wonder how do I reference the user form in which the sender control is located? I have a programmatically created button control and user form in which it's located and when clicking on it I need to reference the user form to grab a value from a combobox in that form to use it as a variable and I can do it descending i.e. get the sub control of the sender but how do you get the control above the sender?
Sub DynamicForm_NewForm_SmartHUB_ProjectsActivated(ByVal sender As Object, ByVal e As EventArgs)
strProjectTypeFolderName = '(form in which the sender is located).combobox_ProjectType.selecteditem
End Sub
You'd first need to cast the sender as Control at least. You can then access the Parent property, although that property is type Control and may not be a form, if the sender is in a Panel, GroupBox or some other container. You ought to call the FindForm method instead. It will return a Form reference and it will also get the containing form no matter how deeply the sender is nested.
If you have Option Strict On, which you probably don't but you definitely should, even a Form reference won't be sufficient. The Form class has no combobox_ProjectType field, so you'd need to cast it as it's actual type - Form1 or whatever - in order to access that field without using late binding.

How to access buttons on a parent form from a child form which inherits it

I made a Windows Form in VB.NET that has a Panel with a Datagridview on it, and underneath it a FlowLayoutPanel with Save and Close buttons on it, call it frmParent. Then I created frmChild and on it I inherited frmParent. I then hooked up my DataGridView to some data and it worked fine.
Next I tried to add code to save changes to the datagrid and the buttons are locked. I checked the modifiers: both buttons and the FlowLayoutPanel that contains them are Protected. I read that there are frequently problems accessing controls that are contained in a collection of another control. But the DataGridView is fine, so my question:
What do I need to do to get the Save and Cancel buttons to be available on the child form? Do I need to do away with the FlowLayoutPanel, or is there another way? In this simple example it isn't that big of a deal, but I would like to start using visual inheritance regularly and I would like to know the best way to do things.
Something like this ?
Public Class frmParent
Private Sub Button_SaveClick(sender As Object, e As EventArgs)
ButtonSaveClicked(...) ' pass here any argument you want (e, sender, anything...)
End Sub
Protected Overridable Sub ButtonSaveClicked(...)
' Do here what frmParent should do upon Button_Save Click...
End Sub
End Class
Public Class frmChild
Inherits frmParent
Protected Overrides Sub ButtonSaveClicked(...) ' same arguments
' Do here what frmChild should do upon Button_Save Click...
' Eventually call MyBase.ButtonSaveClicked(...)
' if you want frmParent to act aswell...
End Sub
End Class
EDIT : For the following parts of the question :
dissociating user interface and object datas
passing an instance of the object or not ?
I'm no expert on programming, but I've read one interresting thing about that user interface/object.
What come to my mind first talking about user interface is a blog post (don't recall the link, sorry) talking about Windows way of programming and Linux way. Before starting to drift to an off topic ride, I want make clear that I don't care about linux vs windows and I'm not interrested in such debate. Just want to share what I remember from this blog post :
In windows programming, the first thing you usually think of (and invited to do) is to create an user interface : the windows form (or the window in WPF) Then you'll create the objects that will be manipulated through that interface. In linux programming, it's the inverse : you'll create first the objects with everything they should be able to do, and only after you'll think of the user interface that will "enhance" the way to manipulate them.
^^ Or alike.. I'm zero linux knowledge, so I can't compare, but I assume the above is true, mostly. And I reckon that since OOP existed, this "windows way" of programming (that I'm used to) has a lot of drawbacks and messy code : you're constantly asking yourself wether a specific part of the code should be enclosed in the object or in the user interface.
So, I've come to an idea of mine, it may be stupid, but I'm fine with. I started to try that "linux way"... at least, "the way I'm thinking a linux programmer does it"
Let's take that control called DataGridView. It can contain text, dropboxes, buttons, images, etc. I'm not digging in bounding Dataset to keep it simple. Basically, the DataGridView is an object having everything to manipulate the above sub-objects within it. Unfortunately, a String, or an Image is not a (visual) control, while a dropbox, or a button is. You have a typical case of windows created object (DataGridView) that is a melting pot of datas and user interface, but you don't have an object that handles only the data part of the DataGridView, and another object that handles only the visual user interface of the thing (or at least, I'm not aware of such objects in .Net) AFAIK You're forced to handle both data and user interface when using a DGV !
So I created a class and simply called it Table_Class that can contain such objects (that are not controls) like String, Bitmap, List (of String), XML... almost anything as the contained datas are binary coded.
Then I created a Form, and called it Table_Form, that can display the content of a Table_Class. But almost all edits are functions and methods that are members of the Table_Class. If I want to order the grid by columns, it's the class that does it through one of its inbuilt function.
Of course, I'm not here to show that I'm wasting my time to reinvent the wheel, I know I'm doing it wrong but this approach is required by the fact I'm dealing with a massive amound of various datas loaded from files and saved as files. I'm just glad I have my own class of datas and happy to extends its capabilities when I want.
The main purpose of this blabla is to illustrate my opinion about passing an entire class or some of its members to a form. In this tandem Table_Class/Table_Form, of course, I'm passing the entire Table_Class on the Table_Form and vice versa. In fact, it's two different thing that can be used at the same time, or one at a time :
' SAMPLE CODE : Entire classes takes several files...
Public Partial Class Table_Class
' here is the object that is invisible to the user
Private _ContainerForm As Table_Form = Nothing
Public Sub ShowForm(Optional ByVal OwnerForm As Form = Nothing)
If _ContainerForm Is Nothing Then
_ContainerForm = New Table_Form(Me)
End If
If OwnerForm Is Nothing Then
_ContainerForm.Show()
Else
_ContainerForm.Show(OwnerForm)
End If
End Sub
Public Sub New()
End Sub
Public Sub New(ByRef NewContainerForm As Table_Form)
_ContainerForm = NewContainerForm
_ContainerForm.SetTable(Me)
End Sub
End Class
And here is the (associated) Form (constructor and associated Object initialization)
Public Partial Class Table_Form
Private _SelectedTable As Table_Class = Nothing
Public Sub SetTable(ByRef NewTable As Table_Class)
RemoveHandlers() ' Table_Class has events.
' If _SelectedTable is not Nothing,
' remove handlers prior to redefining a new selected Table.
_SelectedTable = NewSelectedTable
_SelectedTable.SetContainerForm(Me)
InitializeHandlers() ' Add all handlers for UI updates upon Table_Class edits.
End Sub
Public Sub New()
End Sub
Public Sub New(ByRef NewSelectedTable As Table_Class)
SetTable(NewSelectedTable)
End Sub
End Class
As you can see, I can manipulate the Table_Class either in the background when the user shouldn't care about its content, or via a user interface. With a DataGridView, you must take care of all the visual appearance in order to build an output to the Data/DataSet/Database.
What is important to note here is from the moment you want a visual display of an object through an user interface, you MUST pass critical members of that object to the displaying interface. You can't avoid doing it at a moment or another. In my example, I've passed the ENTIRE Table_Class since I'm doing every data modification in that class.
The difference with a DataGridView is :
with a DGV, when you edit one (String) entry in a Cell using KeystrokeOrF2, the Value is stored in that Cell of that DGV, then, either your application directly sends programatically the value to the associated Database, either you have a save button or a save menu to update the edits.
With my Table_Class, of course I'm using a DataGridView to display its contents, and I have a save button on a toolbar and in a menuitem. But when I click on those buttons, the Save method is not in my Table_Form Form. The Save Method is a method belonging to the Table_Class itself.
That's an example where lies the separation between the visual interface (of the DataGridView/Form) and the object actually containing the datas.
I know this Table_Class/Table_Form example is a complex one, but the way I understand half of the secund part of your question in your original post is : "how do I separate user interface and data/object manipulation"
The answer : it's up to you !
I think we have similar ways of doing it : my Table_Class does everything as your BlackBox Class. My Table_Form shows the datas and your Parent/Child Form so does.
I assume the remaining half of your issue is : "I can't call BlackBox.SaveChanges from the Click method of my button when it is in the FlowLayoutPanel". That's not related to the Object/User Interface limits syndrome, and is an entirely different issue. That's the main purpose of this answer before this long edit : I suggested a workaround by calling a method (ButtonSaveClicked()) instead of writing the save part of the code direclty under the Button_SaveClick() method that handles the click. I don't know if this approach worked for your "locked" button or not.
Back to the user interface/object thing : So comes the above : "you must pass critical members of your object to the user interface at a certain point", and that critical member must be an object (with the critical datas you want to manipulate, in this case, I assume the data to save) In my Table_Class/Table_Form example, I've passed the entire Table_Class in the Table_Form :
Public Partial Class Table_Form
Private _SelectedTable As Table_Class
' ...
End Class
so when I want the Table_Class (object containing data to manipulate) to save edits, through the table Form (form acting as an user interface) I have those methods sets in the Form :
Public Partial Class Table_Form
Private Sub Menu_Main_File_SaveClick(ByVal sender As Object, ByVal e As EventArgs)
' MenuStrip -> File -> Save
SaveTable()
End Sub
Protected Sub ToolBar_Main_SaveClick(ByVal sender As Object, ByVal e As EventArgs)
' A Save Button shortcut on a toolbar.
SaveTable()
End Sub
' ^^ Both controls calls the same method SaveTable()
' This is similar to the ButtonSaveClicked method I initially suggested
' What SaveTable does ?
Protected Overridable Sub SaveTable()
If _SelectedTable IsNot Nothing Then
_SelectedTable.Save(True)
End If
' It just calls the Table_Class.Save(... arguments)
End Sub
' ...
End Class
In the end, the code handling how the datas should be saved doesn't exists in the user interface. To save changes, the Table_Class object has a handfull set of Save(...) methods that handle extension like SaveAs (opens a SaveFileDialog), Overwrite without Prompt, Save as binary or save as textual... And if I want more extensions like sending the datas on the web, in a database, or even packing/unpacking the table contents in a directory, all I have to do is to implement that in the Object (Table_Class) and only the visual part will be handled in the form, the same way I did it from the start.
So when you ask me "Wouldn't I end up with child-specific code in the parent form that way?" I would say : "it's up to you". I've decided that the Table_Class does everything : data loading/parsing, data updating and data saving. If at a time in the future I have to implement a new way of saving the datas, I have the choice to add an overload of the Save() method of my Table_Class, or building a specific Save method in another form for another application using my Table_Class if I want. And that's possible because the Table_Class :
is not an User Interface
has an associated user interface in case it's required
while it has everything to read from and write datas to, programmatically.
and yes, I'm passing the entire Table Class in the UI, no matter it's a parent one or a child form that use inheritance.
I think "having messed parent/child code in your user interface side" highly depends on the capabilities of your non user interface object that (should) manipulate the datas.
(Actually, the Table Class is not a single Table. It's the Node of a Tree of Tables, or a Table that contains datas, and Tables children that contains other datas each. You can think of it like the merger of a DataGridView and a TreeView, that's why the Private variable un the UI Form is named _SelectedTable.)
I'm mentioning that last complexity of the Table_Class to clearly notify that no matter how complex the object is, it's easy to use whatever the user interface you use, because there isn't a single visual code in the object, and there is very little code manipulating datas in the user interface (all the UI does is to read datas from the object, display/edit or use them for other purposes, and send the validated modifications to the object, then call Object.Save())
I've made a long explaination of the way I'm doing it to let you have the latitude to decide how you want your application to behave, what your BlackBox class should be able to achieve alone and what are the limits of your user interface. There is no simple answer to that analysis, and highly depends on who is going to use your application for what purpose; if you have deadlines, keep it simple, but if your goal requires complex and highly manipulable datas, I think you should not mix user interface code and data manipulation code; this is a hell to alter if you have to upgrade your class with new features at a later time.
Sorry for the long post. I'm french speaking and I don't know if my english is okay. Don't mind the blabla, that's just me, can't keep an idea in my mind without telling it in details.
Try the ButtonSaveClicked() call workaround. That worked for me with a flow layout panel : I've made an Overridable ButtonSaveClicked() in the parent Form with a message box saying "Hello", and an Override ButtonSaveClicked() in the child Form saying "Coucou", and it worked well.
Then you can create a variable containing your BlackBox object in the parent form like I did with my Table_Form, and a way to make it accessible from the child form (Protected, or Public Property or a set of Protected methods and functions, I don't know...) Then decide what would be your BlackBox members (properties, methods, functions, events...) and how they should/could be used from the parent and the child forms.
To expose an action on a base class control as an event (another answer shows how to do this via an overridable method):
Base Form:
Public Event SaveClick As EventHandler
Protected Overridable Sub OnSaveClick(ByVal e As EventArgs)
RaiseEvent SaveClick(Me, e)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
OnSaveClick(e)
End Sub
Child Form:
' the event will show up in the event list like any other
' this may be what you want if each child form is supposed to do everything
Private Sub Form1_SaveClick(sender As Object, e As EventArgs) Handles Me.SaveClick
' save some stuff
End Sub
' hybrid where the child form does some stuff, base form does some stuff
Protected Overrides Sub OnSaveClick(e As EventArgs)
MyBase.OnSaveClick(e) ' base form activities
'save some stuff
End Sub
So the base form could raise some abstracted events like SaveItem, NewItem, DeleteItem and the child forms act on that much like how an interface works.
The flaw in this approach is that the controls are still locked on the BaseForm, as they should be (controls are Friend). Each child could pass a parameter to the base form indicating a "mode":
Public Sub New()
MyBase.New("Customer")
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
But to make use of it means that the base form has to have logic to handle all those various "modes" which negates the value of inherited forms specialized to handle Foo, Bar, Customer etc. So, I am not sure form inheritance is what you want: the controls described seem too specialized.
Your best bet, both from a functional standpoint and design standpoint, is to create events in your child form, i.e. OnSaveRequested and OnCancelRequested. In your parent form, declare your child form using WithEvents and create handlers for those events. When you want to save from the child form, raise the OnSaveRequested event and handle it in the parent form (same with Cancel).
Of course, in this scenario your parent form will need to know what to save, so you can either pass the data in as an argument to the event or expose a public property to the data in your child form.

Best Practice For Error Checking Controls On A Form

So I have a form with a variety of different controls (combobox, textboxes, listboxes, etc).
My first thought is to create a If, Else, End If statement. Well while that would work, it could also get pretty long, depending on the amount of controls and combinations.
Validation could include if a listbox is filled, checkbox is checked, etc pertaining to WinForms.
Is there a better solution to check all possiblities than an If statement?
It might be worthwhile to do the error checking as the user fills out the form. This could be implemented with the LostFocus event. Ex:
Private Sub btnTest_Leave(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles yourbutton.LostFocus
Dim txt = yourbutton.Text
If txt = "yourtest" Then
'do stuff
EndIf
End Sub
As above. It depends on the Validation you are trying to do. Are you validating user input, datatype lenght range, etc. Are you validating business rules. Should such and such a value equal something else. There's all kinds of possibilities.

Visual Basic/Studio Text Input issue (disabling certain characters)

I'm creating a form with three text boxes with a 'subtotal', 'amount tendered' and 'change' box. Both subtotal and change boxes are disabled which means the user can only input numbers in the tendered box. I'm using the following code which I found from http://www.vbdotnetheaven.com/blogs/5908/restricting-user-input-in-vb-net.aspx to limit the input:
Private Sub TextBox1_KeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
If Not Char.IsDigit(e.KeyChar) Then
e.Handled = True
End If
End Sub
My concern is that that specific coding works for limiting the input of numbers, but it also disabled the backspace, dot and delete key. I'm fairly new to this (we only started discussing things about VB/Studio less than two weeks ago, I have no prior experience about using Keypresses but I wanted to try)
I've tried manipulating the if statement (and also adding if statements) by excluding the spacebar's ASCII value, only to end up with a warning about incompatible types. I can replace text by double clicking the text input when the form is running, but my app will most likely be utilizing the keyboard mainly because it is a transaction app, so the backspace and more importantly the dot is really needed. How do I exclude both from being disabled?
Take a look at the sample for Control.KeyDown in the MSDN documentation, it's about 95% of what you want.
That said, be aware, there's more work to do. What happens if someone enters 124.34.22? You might consider a MaskedTextBox, but if this is an exercise for learning Visual Basic, that's pretty much cheating :)

VB.Net - How do you "dynamically" select an object?

I'm not sure if the question properly asks what I want, because I'm a bit new, but what I am trying to do is write a sub or function that I can run when a label is clicked, and I want to to be reusable on multiple labels. There will be 12 in total, and I don't wish to write the same thing over and over, with slightly different characters. A programmer never wants to write the same thing twice, right? Also, something else is making it a necessity to do it dynamically.
So, how I was trying to accomplish that was by using a string, and adding the name of the label to the string on click.
click1 = "Label1"
As it turns out, you can't just say click1.Text and return the text of Label1. The reason it's important to do it implicitly is because I need to somehow remember the one I clicked before, to compare the first click and second click, and if they match, do A, and if they don't match, do B.
The first parameter (it is called sender) to the event handler your wrote to respond to the click event is the object which sent the event.
If you assign the same routine to respond to on click of all your labels, it will be called for every one of them, but the sender parameter will point to the actual label which was clicked
HTH
mfeingold is correct, if you're unsure of the syntax:
Private Sub LabelClicked (ByVal sender As Object, ByVal e As EventArgs) Handles Label1.Click, Label2.Click, Label3.Click
sender.Text = "I've been clicked."
End Sub