-
[RESOLVED] Declaring a variable for Form and using it
Could you help me with a problem about forms ? I have declared this variable in a module :
Code:
Public GenericForm As System.Windows.Forms.Form
and then through code I can asign to that variable a specific form . For example :
Code:
GenericForm = Form1
Then I can use that variable to handle that specific form , for example :
My problem begins when I want to handle a control on that form , for example :
Code:
GenericForm.TextBox1.Text = "aaa"
This code creates an error reading : TextBox1 is not a member of System.Windows.Forms.Form .
I have been using code like this in VB6 and was quite useful , but now in VB .NET I cannot :( . You see I have many forms on which there are some text boxes with the same name , so I declare a generic variable as Form and accordingly insert the code the desired text box conform the form I wish each time . Can I do this in VB .NET too ?
-
Re: Declaring a variable for Form and using it
Try using the New keyword
vb.net Code:
Dim frm2 As New Form2
frm2.TextBox1.Text = Me.TextBox1.Text
frm2.Show
-
Re: Declaring a variable for Form and using it
You could make the GenericForm a baseform with all the common textboxes and have the other forms inherit from it. This way common functionality can be placed in this form. Another way is to make an interface IGenericForm with all the common properties (like textboxes) you need and have the other forms implement this interface. It realy depends on how much common functionality you have to choose between a base form and an interface.
-
Re: Declaring a variable for Form and using it
The problem is that every form derives from Windows.Forms.Form. Since every object can be cast to a member of its base, then every form can be cast into a variable of type Windows.Forms.Form. That's what you are doing. However, just because you cast Form1 into a Windows.Forms.Form variable, it is STILL actually the type that it was before (the object itself didn't change, just the type of the variable changed). Therefore, if you use GetType, you can see that the form is still a Form1, but you can't access anything on the Form1 without casting it back to a Form1. You could do that:
DirectCast(GenericForm,Form1).Textbox1.Text
Of course, that really isn't what you want to do, because at that point, you would have to know that it was type Form1, so you might as well leave it as Form1.
Why VB6 actually allowed something like that is beyond me. The language barely pretended to be object oriented, though, and this wasn't the only ugly hack that they allowed. I never used the feature, so I never studied how it worked, but it sure looks like a case of implicit late binding, which makes me rather queasy.
Nonetheless, .NET is OO, so you need to use it in that fashion. If you have lots of forms of this type, I would take the suggestion that guyvdn used in his first sentence....though mostly, I wouldn't, because I wouldn't make a generic form and try to make it fit all situations. What's the advantage?
-
Re: Declaring a variable for Form and using it
Well it is for sure that VB .NET is an object oriented language , unlike VB6 . However things like these make me believe that sometimes VB .NET is less object oriented than VB6 . VB .NET was suppesed to be friendly ... Yet now I have to use something like this :
Code:
Select Case GenericForm.Name
Case "Form1" : Form1.Show()
Form1.TextBox1.Text = "AAA"
Case "Form2" : Form2.Show()
Form2.TextBox1.Text = "BBB"
End Select
However , I am still trying to hope ... At this code :
Code:
DirectCast(GenericForm, Form2).TextBox1.Text = "aaa"
is there any way to replace the Form2 object with a string ? I mean I can retrieve the name of the GenericForm but I can't trick it to receive it :
Code:
DirectCast(GenericForm, GenericForm.Name).TextBox1.Text = "aaa"
Is there a way in the above bold test to , somehow , introduce Form2 ?
-
Re: Declaring a variable for Form and using it
That is not possible, purely because DirectCast is only 'useful' during design-time. The object knows that it is of type Form2 during run-time, but the compiler (and the IDE) does not know this during design-time. That is the reason why you cannot use the name TextBox1 on the variable: the IDE cannot know that your form is of type Form2 which has a TextBox1.
If you could use a string for the DirectCast second argument, then it would have to be evaluated during design-time (otherwise casting the object is useless), but obviously you cannot determine the value of GenericForm.Name during design-time.
That said, I think your whole idea is very non-OOP. If you ever have any need for a 'generic form'*, then you should never need to know anything about the specific form; other than that it's a form. If you do need to know anything more, then you just need to make the variable of type Form2, and not the base type Form.
One solution to your problem is using a base form (as has been suggested), or possibly an interface. If you want to have many forms that have textboxes (they don't need to have the same name) which you want to set in the same way, then you can let those forms implement an Interface that exposes a TextBox property (or better yet: a String property holding just the Text of the TextBox).
vb.net Code:
Public Interface ITextBoxForm
Property TextBoxText() As String
End Interface
Then your forms implement this property:
vb.net Code:
Public Class Form2
Implements ITextBoxForm
Public Property TextBoxText() As String Implements ITextBoxForm.TextBoxText
Get
Return Me.TextBox1.Text
End Get
Set(ByVal value As String)
Me.TextBox1.Text = value
End Set
End Property
'...
End Class
Every form that you wish to use in this way has to implement the property. Then, you can declare your GenericForm variable as an instance of ITextBoxForm. Then you should finally be able to do
Code:
GenericForm.TextBoxText = "ABC"
for any instance of a form that implements ITextBoxForm.
*Be careful with the term 'generic', it usually means something quite different in .NET
-
Re: Declaring a variable for Form and using it
Quote:
Originally Posted by
iliekater
Well it is for sure that VB .NET is an object oriented language , unlike VB6 . However things like these make me believe that sometimes VB .NET is less object oriented than VB6 . VB .NET was suppesed to be friendly.
Actually, what .NET is doing is correct for an OO language. What VB6 was doing was not OO. However, what VB6 was doing worked well for you, and therefore, the non-OO style was more friendly for you than the OO style. Object oriented languages have the drawback that they follow a fairly well defined (though not completely well defined) design. As long as you buy into that OO design, then it will be friendly to you. If you don't buy into it, then a language that works the way you want it to will be more friendly.
Having said that, there is a way to get around this issue, though I hesitate to mention it:
CallByName()
I have never used it myself, but it is used extensively in some code that I maintain. It is taking an object of type Form, and calling a method on that object even though the method is not a member of class Form, but is actually a member of the derived class that the form actually is (Form1 in your first example).
This is basically saying, "Call this method on this object. Trust me, it's there."
I would assume that this requires Option Strict to be OFF (it is custom in the project that I have which uses that construct), as this should require late binding. That alone is less than ideal. You'd be turning off a safeguard to use a hack to get around doing things in an object oriented fashion. For what? If the gain is only that you can avoid typing some code or remembering something, you will lose more by doing this than you will gain. After all, your justification for doing this in the first place appears to be that you don't want to have to do something, though exactly what is not clear, as you would still need to create the different types of forms with the same control (unless you used the derived class suggestion).
-
Re: Declaring a variable for Form and using it
Thank you all for your replies ! Let me ask you a few more questions because , as you can see :bigyello: , I am not an expert !
So , NickThissen , If I got it right the idea is to create a class to emulate Form2 , isn't that so ? I think I should create a class module in which I should create an instance for every form (Form2 , Form3 , FormBlaBlaBla etc) . In everyone of those classes I should Implement each Interface required on each class (form) . Am I right or am I far away ! :o
Shaggy Hiker , could you please help me a bit more with the use of CallByName ? I have used this :
Code:
CallByName(GenericForm.TextBox1, "Text", CallType.Set, "New Text")
but I still receive the same message : "TextBox1 is not a member of System.Windows.Forms.Form ."
-
Re: Declaring a variable for Form and using it
I'm not sure that I can really help with CallByName, and I don't advocate using it. However, the way you have used it is somewhat different from the way it is used in the code I am maintaining. You are calling a method on an object on the form, whereas I was seeing a method of the form being called. Those aren't the same thing. While there may be a way to do it (especially using a call to a control in the controlls collection of the form), an easier solution might be to just add a method to the form called something like SetText, have it change the text property of the textbox object, and just be calling SetText. However, I still thing this is something to do as a last resort, and not something to pursue until everything else has been tried, including coming up with a new design.
As for the other part, I think you may be far away, but we run into the problem that words can mean different things to different people. So a bit of groundwork:
A form that you design isn't an object, it is a type of object. You can create an instance of that type with something like:
Dim nf as New Form2
At this point, nf is an object, and it is an object of type Form2. Unfortunately, beginning with .NET 2005, MS added default instances such that when you design Form2, MS quietly creates an instance of type Form2, and calls it....wait for it....Form2. So behind the scenes, MS is writing this line:
Public Form2 as New Form2
You won't find the actual line anywhere (or else most of us would delete it out of spite), but it is effectively there. This sows all kind of confusion, because it makes it look like forms are special objects that don't always have to get instantiated to use. They DO have to be instantiated, it's just that MS is doing that for you behind the scenes. I hate that, but it may not even be relevant here.
A type can inherit another type, and ALL forms do actually inherit another type. Every form you create inherits Windows.Forms.Form as a base type. Therefore, you can find:
Public Class Form2
Inherits Windows.Forms.Form
Because of this inheritance, you can cast your forms back to Windows.Forms.Form, because all objects of a type can be seen as an object of their base.
What people are suggesting is that you create a special form that has the textboxes you want. Call that special form FormBase. Since FormBase is a form, it will inherit from Windows.Forms.Form, but that's irrelevant. You would then create Form2 with
Public Class Form2
Inherits FormBase
Then you can create your genericform as type FormBase, since that is a base class of Form2 (you could also create your generic form as type Form, because that is a base class of FormBase which is a base class of Form2, but you already know that won't do what you want, so stick with FormBase). If TextBox1 is a member of FormBase, then you will be able to call methods on the generic form that deal with TextBox1.
The Interface alternative is a bit different, but also a bit more versatile. I'll leave that for another time, though.
-
Re: Declaring a variable for Form and using it
Quote:
Originally Posted by
Shaggy Hiker
Actually, what .NET is doing is correct for an OO language. What VB6 was doing was not OO. However, what VB6 was doing worked well for you, and therefore, the non-OO style was more friendly for you than the OO style. Object oriented languages have the drawback that they follow a fairly well defined (though not completely well defined) design. As long as you buy into that OO design, then it will be friendly to you. If you don't buy into it, then a language that works the way you want it to will be more friendly.
Having said that, there is a way to get around this issue, though I hesitate to mention it:
CallByName()
I have never used it myself, but it is used extensively in some code that I maintain. It is taking an object of type Form, and calling a method on that object even though the method is not a member of class Form, but is actually a member of the derived class that the form actually is (Form1 in your first example).
This is basically saying, "Call this method on this object. Trust me, it's there."
I would assume that this requires Option Strict to be OFF (it is custom in the project that I have which uses that construct), as this should require late binding. That alone is less than ideal. You'd be turning off a safeguard to use a hack to get around doing things in an object oriented fashion. For what? If the gain is only that you can avoid typing some code or remembering something, you will lose more by doing this than you will gain. After all, your justification for doing this in the first place appears to be that you don't want to have to do something, though exactly what is not clear, as you would still need to create the different types of forms with the same control (unless you used the derived class suggestion).
AAAGH! NOT CALLBYNAME!!!
Turning Option Strict off is just fine if you absolutely need to do that. No need to use CallByName!!! :)
Without Option Strict, you can use late binding. In fact, I make use of it all the time! It makes your code less readable to have too many DirectCasts/CTypes. If you ever need to use it a lot, late binding/casting is actually good!
-
Re: Declaring a variable for Form and using it
Pretty much my opinion, too, but it should be put on the table, even if only with reluctance.
-
Re: Declaring a variable for Form and using it
Well I thing I did it ! Though I confess I don't understand it completely and that's why I'd like your comments about it .
Instead of declaring a Form variable like this :
Code:
Public GenericForm As System.Windows.Forms.Form
I declared it as Object
Code:
Public GenericForm As Object
The only thing left was to also declare the TextBox1 as public . I changed its declaration in Form2.Designer which was like this :
Code:
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
into this one :
Code:
Public WithEvents TextBox1 As System.Windows.Forms.TextBox
I thing this is called late-binding , but please don't lough at me , I am not sure what it's called and what it means !
And one more thing : how can I humanly access Form2.Designer ? In order for me to access it , I had to use the Search form . There has to be another way , I feel like a fool getting there through the Search form :o . I tried right clicking on the form In the Solution explorer) but nothing ...
-
Re: Declaring a variable for Form and using it
Late binding is not very good if it isn't absolutely required, but ok...
Also, you should not change the designer code manually (that is why it is hidden). To change the accessibility of a control, you can set its Modifier property. This is essentially the same as changing the Friend keyword to Public manually, except that your change will be persisted. When you change the designer manually, you might see that the keyword Public will change back to Friend automatically when the designer is re-generated, so your changes are lost.
That said, if you absolutely want to see the designer, you just have to click the 'Show All Files' button (located in the toolbar of the Solution Explorer). The Form treeview item will now be able to expand and you can see the designer file.
-
Re: Declaring a variable for Form and using it
Thanks again NickThissen . That was a very helpful conversation .
-
Re: [RESOLVED] Declaring a variable for Form and using it
Yeah, it's late binding. You've never given a good justification for what you are trying to do, though it was never absolutely necessary, either. I trust that you are doing this because it makes some kind of sense in the overall design and not just because VB6 allowed it, or because it seems easier, since this action will allow you to freely introduce other bugs that are hard to diagnose. It's a hack. Sometimes hacks are necessary (especially as moderators), but they should only be used as a last resort (as should moderators).
-
Re: [RESOLVED] Declaring a variable for Form and using it
Here's what I need to do (and what I was doing easily in VB6) : I have 10 forms . Lets say Form1 , Form2 , Form3 ..... and emmm I guess Form10 ( ! ) . Each form actually is used for specifying data for completely different things . Form1 is used to insert data for Rooms, Form2 is used for data for Windows , Form3 for thermal corps etc etc etc . In fact even when those forms are loaded or unloaded , they do unrelated things . Thus , it is mandatory to be as different forms .
However , There are some common elements , like exterior temperature , Level etc . They all can set through another form , lets call it Catalog .
So , each time this catalog is been called by one of these forms (Form1 , Form2 etc) there is line of code in each of them specifying which was the form calling that Catalog (i.e. LastForm = Form3)
Therefore , when the user picks up a value from the Catalog form , the only thing I have to do is to simply use :
LastForm.TextBoxLEVEL = ListBox.Text
You understand that if I didn't do that , I'd have to use a huge Select case statement in order to select the case conform the name of the form and that statement would have grown even larger when considerating that there isn't only one control that transmits data to the previous form but quite many controls (textboxes , listboxes etc) .
-
Re: [RESOLVED] Declaring a variable for Form and using it
I suggest you have another look at the interface approach, NickThissen gave a good example to get you started in post #6. It is exactly what you need, it will perform a lot faster and it is the only way to make absolutely sure that all your forms have all the right textboxes (or properties) on them. If you want to be a good .net programmer you should forget about the VB6 approach and try to learn the new stuff.
-
Re: [RESOLVED] Declaring a variable for Form and using it
I'd be looking at interfaces, too.
If you implement an interface on a class (a form in your case), then you are guaranteeing that the features of the interface are present. Technically, you can do this by just coding consistently, which is what you were doing in VB6 (if you forgot to put a certain control on the form, the code would just crash, so you tended to get it right pretty quickly). In .NET, the interface is an actual code construct that you can define. So in your case, you would define an interface that has a set of items that you guarantee will be there, and when you add the Implements IMyInterface line to the top of the class (by convention all interfaces start with I), then the stubs of all those necessary items (if they are methods) are created for you. You can't get away with not adding them, so the compiler will check that you have what you need to have, and will even generate at least the outline, for you.
However, interfaces have another useful feature for what you are doing. As you know, any object can be cast to a member of its base class, but any object can also be cast to any interface that it implements, as well. Therefore, while you couldn't get this to work freely:
Public GeneralForm as Windows.Forms.Form = Form2
if form2 implemented an interface called IMyInterface, then you would be able to do this:
Public GeneralForm as IMyInterface = Form2
Now you can call those members that are part of IMyInterface through GeneralForm.
-
Re: [RESOLVED] Declaring a variable for Form and using it
So all you are using the 'LastForm' variable for is to have a reference to the form that called the Catalog form? In that case, I suggest you use an entirely different approach.
I am assuming the Catalog form is some kind of simple dialog form. Let's assume for the sake of example that it has a ListBox on it which you use to select something. For example; suppose the Catalog form displays a list of persons, and the user is supposed to select a person and click OK to choose that person. When a person has been chosen, his name should appear in a TextBox on some entirely different form (the form that called the Catalog form).
In your approach, you would handle both the choosing of the person and setting the TextBox of the calling form in the Catalog form. This is generally not a good idea! Instead, your Catalog form should only handle things specific for that form: selecting the person. Assigning the name of that person to some TextBox is not a job for the Catalog form. This should be obvious from your problem: the Catalog form does not know about the calling form, and there could be many different calling forms.
Instead, the assignment of the name of the selected person to some TextBox should be done in the calling form. Doesn't that seem much more obvious?
The Catalog form would expose a property that provides the information for the calling form. In this case, a PersonName property which simply returns the selected item of the ListBox:
vb.net Code:
Public Class CatalogForm
Public ReadOnly Property PersonName() As String
Get
Return ListBox1.GetItemText(ListBox1.SelectedItem)
End Get
End Property
Private Sub OK_Button_Click(...) Handles OK_Button.Click
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub
Private Sub Cancel_Button_Click(...) Handles Cancel_Button.Click
Me.DialogResult = DialogResult.Cancel
Me.Close()
End Sub
End Class
(The OK_Button and Cancel_Button click events should be automatically generated if you add the CatalogForm using the Dialog Form template)
Now, to show the Catalog form (and provide the user with the 'person choice' screen), you simply use the ShowDialog method of the Catalog form. The form will be shown as a modal form (meaning it cannot lose focus until closed). The user selects a person's name and then clicks OK.
The calling form then sets its own TextBox text:
vb.net Code:
Public Class Form1
Private Sub ChooseUser()
'use a Using block to create a new instance of the Catalog Form
Using f As New CatalogForm()
'only continue if the user clicked OK (not cancel)
If f.ShowDialog() = DialogResult.OK Then
'use the PersonName property of the form to assign the textbox text
Me.TextBox1.Text = f.PersonName
End If
End Using
End Sub
End Class
That's it! The CatalogForm doesn't know anything about the form that called it, and it doesn't need to either. That is one very big and obvious advantage. The other is that you can use this same code wherever you want, in any form you want. The CatalogForm is a 'stand alone' entity that can be used to select a user, no matter what the circumstances are.
This is exactly how object oriented design works, not the other way around.
You can compare this to an OpenFileDialog (which you may have used before). It is completely analogous (if not completely the same). The OpenFileDialog can be seen as a form (although technically it isn't, but it shows a form). You can call its ShowDialog method wherever you want, and retrieve the selected filename using the FileName property:
vb.net Code:
Using ofd As New OpenFileDialog()
If ofd.ShowDialog() = DialogResult.OK Then
Me.TextBox1.Text = ofd.FileName
End If
End Using
See? It's the same! Of course the OpenFileDialog knows nothing about your form, and nothing about the TextBox that is going to be used to display the selected filename. If it worked the way you are trying, then it wouldn't work at all as there would be no way to tell the OpenFileDialog which TextBox to fill.
The only purpose of the OpenFileDialog (CatalogForm) is to select a file (person). Whenever selecting a file (choosing a person) is required, you use the ShowDialog method to show the form and let the user select the file (person). Then, the OpenFileDialog (Catalog form) returns the selected file (person) via the FileName (PersonName) property, and you can use it however you like.
Of course, you mention that the catalog form needs to pass other data as well. But is that a problem? Of course not; you simply create a property for every piece of data you want to pass.
-
Re: [RESOLVED] Declaring a variable for Form and using it
NickThissen, this way you will have to put the code in all 10 forms which he is trying to avoid. Or create a base form and have all 10 forms inherit from it as was my other suggestion at the beginning of the thread. It really comes down to the other code that is common for the forms or for the catalog form.
-
Re: [RESOLVED] Declaring a variable for Form and using it
Thanks again for your replies ! Please tell me something , in order to understand it better : the Using keyword is some kind of statement ? What does it do ? I never heard of it before .
-
Re: [RESOLVED] Declaring a variable for Form and using it
Quote:
Originally Posted by
guyvdn
NickThissen, this way you will have to put the code in all 10 forms which he is trying to avoid. Or create a base form and have all 10 forms inherit from it as was my other suggestion at the beginning of the thread. It really comes down to the other code that is common for the forms or for the catalog form.
The only code that has to be duplicated is the single If statement in the Using block and getting the value from the forms property. That is 3 lines if you use the default notation, or quite possibly one line if you cram the If statement into a one-liner. I agree that code duplication should be avoided, but carrying that statement this far is too much in my opinion. Every dialog form in .NET (and in fact I think I can safely say, every dialog form in OOP design) works the same way.
If iliekater is trying to do what I think he is (eg: using a dialog form to let the user select some information, and using that information differently in different circumstances), then he should absolutely use the dialog form approach I suggested. It is the only way that makes sense.
The Using block is basically a 'shorthand notation' for declaring a variable of a type that can be disposed. The following:
Code:
Using x As New SomeDisposableObject()
'...
End Using
is more or less equivalent to
Code:
Dim x As New SomeDisposableObject()
'...
x.Dispose()
You can only use it if the type implements IDisposable (eg: if it is disposable).
Since a form (when shown as a dialog with the ShowDialog method) does not dispose itself when closed, you should handle the disposing. You can either dispose it manually, but this is often forgotten. It's easier to use a Using block, which handles the declaration of the object and the disposing in one go.
Also, and I am not too sure about this, I think the Using block always disposes the object even if something goes wrong. Don't take my word for this, but I think that if the '... part in the examples above throws an exception (eg: executing stops), then the Using block will still dispose x, while the regular way (without Using) will not (because the call to x.Dispose is never made).
-
Re: [RESOLVED] Declaring a variable for Form and using it
Yes Nick , you got it right . That's what I was trying to do . Thanks again for your kind reply . I'm off to put all these things to work !
-
Re: [RESOLVED] Declaring a variable for Form and using it
NickThissen you are right, both about the solution and the using block, it does dispose the object when an error occurs and thus saves you from typing a try catch block.