Get the name and properties of controls dynamically - vb.net

I have 10 TrackBar1 TrackBar2, TrackBar3 ..... TrackBar10. What I want is how to manipulate it.
I Have problem with access to TrackBar control property Min, Max, Value ....
For i = 1 to 10
' its OK
Me.Controls("TrackBar" & i.ToString("0")).Text = "Test1"
' Error -Min is not a member "System.Windows.Forms.Control"
Me.Controls("TrackBar" & i.ToString("0")).Min = 10
Next

When you index the Controls collection, you get a Control reference back. That collection can store any type of control, so the only thing that they all have in common is that they inherit the Control class. That means that you can access any members of the Control class directly. Text is a member of the Control class so you can access the Text property directly. Any members that are more specific than that require a cast as the more specific type in order to access them. If you want to access members of the TrackBar class then you need a reference of type TrackBar rather than type Control, which means casting as that type.
Dim c = Me.Controls("TrackBar" & i)
Dim t = DirectCast(c, TrackBar)
To explain casting, I always use the example of taking a pet to the vet. A vet can treat all types of animals but, when you take your pet, it is a specific type of animal. When you present a box to the vet, all they know is that there's an animal inside, so all they know that they can do for it is the things that apply to all animals. Once you take your pet out of the box and they see what type of animal it is, they then know that they can do all the things specific to that type of animal. Your pet didn't magically change from one type to another but the vet now knows all the specific things it can do for it as well as the general.
That's how casting in programming works. Your TrackBar was always a TrackBar but, because it was initially accessed via a Control reference, the compiler only knew that it can do things with it that are common to all controls. The cast informs the compiler of the specific type so the compiler knows that it can do things with it specific to that type.
One option to avoid the explicit cast is to just get the TrackBars in the first place:
For Each tb In Me.Controls.OfType(Of TrackBar)()
'Use tb here.
Next
The OfType method will filter out all items not of the specified type and will return references of the specified type, so tb is type TrackBar in that code. You'd have to use a For Each loop in this case, so you have less control over the order the items are accessed. They will be returned in accordance with the z-order on the form. That means that if you added them in ascending numerical order, which seems likely, and did not change the z-order, you'll still get them back in ascending numerical order.

Related

Why did I have to manually set the .value from the .text on losing focus in an unbound textbox

I have an unbound textbox to accept the delete older than: number of days. It is in the report header. I set it to 30 days but I want the user to be able to change it. I was banging my head trying to figure out why entering 40 was not being accepted and it reverted back to 30 every time. I finally decided on using the lost_focus event to set .value to .text. That worked.
Further research showed that when the textbox get's focus text and value are both the same, 30 in my case. Changing the number in the text box to 40 shows the values of text at 40 and value at 30. Unless I specifically set Value to the value of text Access changes text to the value of value. This is different behavior than other places in Access such as forms.
Can anyone tell me why this might be? I can't find any setting that might do this. Is it because it's in a report header? what is the difference between this and every other text box I've ever used?
From a "best practices" viewpoint, Access Reports are not intended to be used interactively despite the ability to manipulate some unbound controls. Although workarounds can be implemented that function sufficiently well, such solutions are often incomplete and buggy and function differently depending on the active view: Report View vs. Print Preview. Appropriate design patterns include using Access Forms for specifying report options which then open the Report in a static configuration.
This may not satisfy the question "Why?" if seeking a deeper answer as to why Microsoft implemented inconsistent binding behavior in Access, or why they allowed interactive controls in reports at all if they don't behave the same way as in forms. But Access has plenty of other quirky behaviors that have no known/published explanation.
Regarding the priority of the Value property updating the Text property (and not vice versa): Value is the key field because it contains the actual data for the control (bound or unbound). Although it is natural to have a single control for both display and input (uh, that's how almost all controls work), the processes of displaying data and parsing user input are two distinct functions. The visual representation returned by the Text property can be manipulated using the various formatting properties, and technically could display an incomplete representation of the underlying Value data. If there are any conflicts between the stored Value property and the Text property, it is natural that the existing Value property has precedent.
My guess is that the automatic binding behavior was "relaxed" for reports to allow more flexible custom reporting output. First consider an Access Form in Datasheet view: An unbound Form control shows the same value for all records. Even if the control is edited while on a particular row, the updated value is displayed for all rows. The same control object is essentially repainted for each row and there is no concept of individual instances of the control that can hold different values. Bound controls have built-in code that repaint the control with data from the particular row, but there are still not multiple instances each "holding" the individual values. The visual output differs from an intuitive object-oriented paradigm where our minds what to assign each visual row its own in-memory instance of the controls--it just doesn't work like that in Access.
Unlike the Form behavior just described, the Report's Print Preview (and actual printed output) allows unbound controls to display different data per row using the Detail_Format() event. Within the Detail_Format() event, one can set the Value property of a control at which time the Text property is automatically updated according to various formatting properties. This update Text is then output for the current row. Perhaps (just guessing) that this behavior would not function properly if the Text property updated the value property. I suspect it would cause recursive events during report generation. Because reports are not meant to be interactive, relevant text-input parsing code was "disconnected" so that it doesn't behave like on a form.
All that explanation doesn't make Access any less frustrating nor remove its limitations, but at least learn to adapt and design things in the "Access-esque" way rather than fighting it.
your best bet is to design a form with the unbound combo boxes and have your data displayed in a subreport. I like to design my reports so that when values are updated the query for the recordsource of the report is generated doing this requires 2 queries to exist, one with all data possible and a filtered one as subreport recordsource. This will control the data for printing and also allow users to close or navigate away from the report and return to the data later.
Private Sub ComboBox1_AfterUpdate()
Dim Query1 as Object
Dim Temp_Name as Variant
Temp_Name = SubReport.SourceObject
SubReport.SourceObject = Empty
Set Query1 = Me.Form.Application.DBEngine.Workspaces(0).Databases(0).QueryDefs ("SubReport_Query")
Query1.SQL = "Select * Unfiltered_Query WHERE Field1 <= " ComboBox1 & ";"
SubReport.SourceObject = Temp_Name
End Sub

What does Property Let means in VB?

I have often come across code that looks like this.
Public Property Get MyProperty() As String
MyProperty = m_MyProperty
End Property
Public Property Set MyProperty(ByVal value As String)
m_MyProperty = value
End Property
Or even:
Public Property Get MyProperty() As String
Return m_MyProperty
End Property
And I had never ever met with this.
Public Property Let MyProperty(ByVal value As String)
m_MyProperty = value
End Property
Is the Let keyword the same as Set ?
According to this post, it seems so.
Properties in VB
MSDN states:
Property Let Statement
Declares the name, arguments, and code that form the body of a Property Let procedure, which assigns a value to a property.
Property Set Statement
Declares the name, arguments, and code that form the body of a Property procedure, which sets a reference to an object.
In addition to it, as per MSDN definition:
Property
A named attribute of a control, field, or object that you set to define one of the object's characteristics (such as size, color, or screen location) or an aspect of its behavior (such as whether the object is hidden).
Object
Objects seen in a Microsoft Access database:
tables, queries, forms, reports, macros, and modules
objects that are defined by the system, such as the MSysObjects table
Objects seen in a Microsoft Access project:
objects stored in the Access project file: forms, reports, macros, and modules
objects stored in the Microsoft SQL Server database: tables, table properties (such as > - indexes, triggers, keys, constraints, defaults, rules, and user-defined data types), views, stored procedures, and database diagrams
objects defined by the system, such as the sysindexes table
Objects seen in both Access databases and Access projects:
data access pages, which are shortcuts (displayed in the Database window) to corresponding HTML files stored in the file system
information from another application, such as a chart (graph) or a drawing
Except that the context under which I have seen the Property Let Statement used, there is no Access or whatsoever else.
Thanks for the received comments. They guided me to MSDN, though I still can't get the meaning of the Let statement, apart that it might be interchangeable with the Set statement, as I see it.
Are they both really interchangeable?
You pretty much answered your own question.
As you can see from the MSDN docs, a Let declaration for a property is used to assign a value, i.e. to be used for any datatype except for objects (for which you would normally assign through Set anyway).
So, following the same logic, a Set declaration would be used if you want/need to be able to assign objects, and only objects (typed or not) to a property.
So, let's say you have:
Property Let MyFirstProperty(NewValue As String)
m_sMyFirstProperty = NewValue
End Property
Property Set MySecondProperty(NewValue As Object)
Set m_oMySecondProperty = NewValue
End Property
Then the following won't work:
' This won't work
MyFirstProperty = CreateObject("...")
' Neither will this
Set MyFirstProperty = CreateObject("...")
' Also, the following won't be allowed
MySecondProperty = "My new string"
But the following two statements will:
MyFirstProperty = "My new string"
Set MySecondProperty = CreateObject("...")
Note that you can have both a Let and Set declarations for the same property at the same time, depending on what you want to do:
You could handle objects being assigned to the property differently (through the Set declare) than for other datatypes (using the Let);
Or, if your internal variable for the property is a Variant, and you want to allow both objects and scalar values to be assigned to the property.
In short, then:
If you want to assign values to a property (everything but objects), add a Let handler for the property.
If you want to assign objets to a property, you'll need a Set handler for the property.

Excel Indirect() type function For VB.net?

In excel I could say =INDIRECT("A" & G3) where G3 had a value of 4 and my cell would then refer to A4. What I am looking for is a similar kind of function for VB.net.
Is there a way to refer to a different variable based on a variable. EG. first pass I was to refer to txtJobNum1, txtBatNum1, and lblBat1. on pass two txtJobNum2, txtBatNum2, and lblBat2. If it were only a few, 3-4 maybe, it wouldnt be bothersome, but it's 50. The best I have come up with now to work around is build a class that holds references to those objects and make an array of that class. Below is an example table showing What I want to make with a given input number.
You can see how if I could make use of an "INDIRECT" function It could potentially shrink down to a 5-6 line loop instead of 200 lines of just variable assignments.
my concept of how it would work
BatchGroups(<NUMBER>).Label = lblBatNum<NUMBER+1>
0 BatchGroups(0).Label = lblBatNum1
0 BatchGroups(0).Number = txtBatNum1
0 BatchGroups(0).Quantity = txtQtybat1
0 BatchGroups(0).JobNumber = txtJobNum1
1 BatchGroups(1).Label = lblBatNum2
1 BatchGroups(1).Number = txtBatNum2
1 BatchGroups(1).Quantity = txtQtybat2
1 BatchGroups(1).JobNumber = txtJobNum2
2 BatchGroups(2).Label = lblBatNum3
2 BatchGroups(2).Number = txtBatNum3
All of the controls are stored in the Controls collection of their parent, and the controls in the Controls collection are addressable by name, like this:
Dim theControl As Control = Me.Controls("txtJobNum" & theNumber)
(Where Me is the Form) If the controls are in some other container control, such as a panel, you can get to them through that container control's Controls property, for instance:
Dim theControl As Control = MyPanel.Controls("txtJobNum" & theNumber)
However, having 50 some-odd controls like that sounds like it may be a bad design anyway. It may be better to consider having a grid, or an editable list of some sort. If you must have all of the separate controls like that, it would probably be better to dynamically load them in a loop, and then store references to them in a list of BatchGroup objects as you were thinking. It would be much easier to to that in a loop because you'd only write the code once rather than separately for each batch group.
More generally, the term in .NET for what you are asking is called "Reflection". However, using reflection can cause your code to be more brittle and it is also not very efficient, so, when there are other alternatives, is is the case here, it is usually best to take one of them, rather than resorting to reflection.
Create a dictionary of your objects by Name. Then use TryGetValue to retrieve. In this case it would expect a String value as a Key, so you can have a custom naming scheme, which maps 1-to-1 onto your controls list.
Read more about a Dictionary class on MSDN:
Dictionary(Of TKey, TValue) Class
You could use .Controls of the parent container, but then your controls could be nested in each other, so you'd have to use recursion or linearize into flat list, either adds complexity and maintainbility effort, and reduces performance. Dictionary is the fastest, especially if your Key is a string.

Add Property to Properties Window

I have built a UserControl class and am exposing certain properties to the parent object.
When i drag and drop the component to the parent objects designer surface, i get the effect i need (per say). What i want to further develop is the ability to pre-fill the property value (in properties window) as a default but it isnt auto-populating as i would have expected.
Here is what i have so far:
<Browsable(True), Category("Data"),
DefaultValue("01/01/1990")>
Public Property [Date] As String
Get
Return Me._dt
End Get
Set(value As String)
Me._dt = value
dtValue.Text = value
End Set
End Property
I understand that DefaultValueAttribute assigns the value if no other assignments are made, but thought it would also place that value in the field in the Properties Window.
As well, this ultimate assignment will go up one more level as a collection, so any advise or URL's for how to make a Collection of Components available via Properties Window?
I understand that DefaultValueAttribute assigns the value if no other assignments are made
That is incorrect.
The DefaultValueAttribute is just metadata that tells the designer what the default is.
It is still up to your code to ensure that the property actually gets that value.

Why can't I get properties from members of this collection?

I've added some form controls to a collection and can retrieve their properties when I refer to the members by index.
However, when I try to use any properties by referencing members of the collection I see a 'Could not set the ControlSource property. Member not found.' error in the Locals window.
Here is a simplified version of the code:
'Add controls to collection'
For x = 0 To UBound(tabs)
activeTabs.Add Item:=Form.MultiPage.Pages(Val(tabs(x, 1))), _
key:=Form.MultiPage.Pages(Val(tabs(x, 1))).Caption
Next x
'Check name using collection index'
For x = 0 To UBound(tabs)
Debug.Print "Tab name from index: " & activeTabs(x + 1).Caption
Next x
'Check name using collection members'
For Each formTab In activeTabs
Debug.Print "Tab name from collection: " & formTab.Caption
Next formTab
The results in the Immediate window are:
Tab name from index: Caption1
Tab name from index: Caption2
Tab name from collection:
Tab name from collection:
Why does one method work and the other fail?
This is in a standard code module, but I have similar code working just fine from within form modules. Could this have anything to do with it?
Edited to add
formTab was declared as a Control, but I find that if it is declared as an Object then the code works.
This will probably solve my problem, but in the interests of furthering my knowledge I would be grateful for any explanation of this behaviour, particularly with regard to the difference in running the code in the different types of module.
This is a really great question. Your edit at the end of the post reveals a lot about how VBA works and what's going on here. I'm not 100% this what's going on, but I'll explain what I think is happening.
A Collection in VBA (and VB6, for that matter; same code base) is not strongly typed. This means that everything in a collection is technically an "object." In the .NET world (as of .NET 2.0), it's possible to have strongly typed collections so that you could say "everything in this collection is a Control object." In VBA, this isn't possible with a Collection.
In your first iteration, where you are referring to the item indexed in the activeTabs collection, activeTabs(x + 1) is referring to an object. When you tell VBA to look up .Caption of that object, it doesn't know what the underlying type is (I think), so it has to simply look to see if the underlying object type contains a property or method called Caption. As you can see, Tab controls do in fact contain a property called Caption.
In your second interation, where you are doing a For Each loop, I think the problem is that the Control type probably doesn't have a property called Caption, though different types of controls probably do. For example, a text box control probably doesn't have a Caption property whereas as label control does have a Caption property.
You have a few options to fix your second loop. 1) You could declare formTab as a Tab control (I'm not sure exactly what it's called). The Tab control should have a Caption property. 2) If every control in activeTabs is not specifically a Tab control (in which case, you should probably call it activeControls instead of activeTabs), you could check within your loop to see if the formTab is actually a Tab control. If it is, cast it as a Tab control and then call .Caption. Until you cast it as a Tab control, VBA won't know that it has a Caption property since a regular Control object doesn't have a caption property.
In the end, you can get away with using objects as in your first loop and letting the runtime figure out what to do, but that can give really bad performance. In general, it's better to work with your specific types in a strongly-typed language. It also helps to show in your code that you know specifically what you're working with rather than leaving it to the runtime to decide what properties and methods you can work with.