I have a number of resources called
My.Resources.diceDark1
My.Resources.diceDark2
My.Resources.diceDark3...
I want to do something like
For i as integer = 1 to 10
button1.backgroundimage = My.Resources.diceDark i
Next
So it will cycle through all the resources and change the background image to that
The Designer creates property getters and setters for the images etc you add to Resources. So, for an image named dicedark1.jpg, it creates:
Friend ReadOnly Property diceDark1() As System.Drawing.Bitmap
Get
Dim obj As Object = ResourceManager.GetObject("diceDark1", resourceCulture)
Return CType(obj,System.Drawing.Bitmap)
End Get
End Property
You can see these in Resources.Designer.vb. So the resource "names" you use are not something like variables, but Property names for the Resources object. But what you can do, is what you see in the getter, which is use GetObject:
Private DiceNames As String() = {"diceDark1", "diceDark2", "diceDark3" ...}
...
' assuming you have control refs in an array also:
For i As Int32 = 0 To 6
picBox(i).BackgroundImage = My.Resources.ResourceManager.GetObject(DiceNames(i))
Next i
The property wrappers obviously make it easier to get at your resources. To use the loop, you'll need the target controls in an array or list since picBox + 1 or any variation thereof wont work any better than the My.Resources.DiceDark i reference.
Related
In the below code "f" is an instance of the Class FORM which has a property "s" of type SIZE, a structure which has been defined in the code. My question is: When I try to assign values to the attributes of property "s" of the instance "t" directly it does not work: That is the statement f.s.height = 15 does not work. My confusion is arising from the fact that when I print the values of the property "s" of the instance "f", I am able to print the individual attributes of the structure SIZE but the same cannot be done while assignment of value. Assignment of values require me to call the constructor. Why is it so? What is preventing the assignment of the value to the attributes of "s": i.e. f.s.height & f.s.width?
Module Module1
Sub Main()
Dim f As New MyForm()
f.s = New Size(2, 5) 'Works Fine
f.colour = "Red" 'Assignment works just fine
'Below: Individual elements cannot be acceessed for assignment. WHY?
f.s.height = 15 'Doesn't Work
f.s.height = +2 'Doesn't work
'Individual elements can be accessed while printing
Console.WriteLine("Widht = {0}", f.s.width)
Console.WriteLine("Height = {0}", f.s.height)
Console.ReadLine()
End Sub
End Module
Class MyForm
Public Property s As Size
Public Property colour As String
End Class
Public Structure Size
Dim height As Integer
Dim width As Integer
Public Sub New(ByVal w As Integer, ByVal h As Integer)
width = w
height = h
End Sub
End Structure
Pls help.
The compiler should be indicating "Expression is a value and therefore cannot be the target of an assignment".
Change Size from a Structure to a Class (and Dim to Property) to fix the problem:
Public Class Size
Property height As Integer
Property width As Integer
Public Sub New(w As Integer, h As Integer)
width = w
height = h
End Sub
End Class
By the way, you'll also see this behaviour with the standard System.Drawing.Size which is defined as a Structure rather than a Class. (So is Point and probably others.)
This behavior is fundamental to value types (Structures). Conceptually, an instance of a value type is supposed to represent a single immutable value, and any instances with the same value are all supposed to be equivalent. As you have observed, you can get very surprising behavior if you try to change parts of an existing value type. It's really not intended for you to be able to alter them piecewise.
For this reason, I will always recommend that the members of a value type should be marked as ReadOnly so that you get blocked from trying to change them after construction.
If you want to be able to treat something like a mutable object instance instead of an immutable value, it needs to be a reference type (a Class). That's what they're designed to do.
After a lot of searching I came across the following article which probably explains the reason why we are not able to directly access the height/width attribute of the SIZE structure through an instance of the FORM class. Requesting people to go through this as the author has given a lot of details:
http://www.albahari.com/valuevsreftypes.aspx
PLs feel free to share any difference in opinion.
I can't find anything on this anywhere so here's my problem (although it is mostly a cosmetic one):
I have a class being used as a custom data type, but when I look in the locals or watch window I see that each Property Let has created an extra variable, which clutters the window with redundant variables and information (and potentially takes up extra space).
Example:
In class module Class1:
Private data As Integer
Property Get X() As Integer
X = data
End Property
Property Let X(ByVal Value As Integer)
data = Value
End Property
And to test:
Sub Test1()
Dim TestClass As Class1
Set TestClass = New Class1
TestClass.X = 100
End Sub
In the locals window:
Am I supposed to be recycling this extra variable somehow or am I doing something else wrong?
--- If you look at stock Excel objects (like a Worksheet) there are no duplicate variables whatsoever.
Edit: To clarify, I want to know if there is a way to hide the Property in the locals/watch window to make them easier to navigate.
It's fine. data is your private variable and X is just property. Nothing wrong about that. But you should consider naming convention, setters and getters for private variable should be somehow consistent, for example:
'Member variable, pName - private Name
Private pName As String
'Properties
Property Get Name() As String
Name = pName
End Property
Property Let Name(val As String)
pName = val
End Property
If I have a class object A, and it has properties such as a0, a1, a2... If this class has 100 properties like this (up to a99). I would like to display each of these properties, but I do not want to have 100 lines of code of calling this as following
print A.a0
print A.a1
print A.a2
...
print A.a99
The code is too inefficient, so I am wondering if there is a way to loop through these properties. Thank you.
.NET provides the ability to examine an object at runtime through a process known as reflection. The purpose of the original post was to iterate through an object's properties in an automated fashion rather than by manually coding explicit statements that displayed each property, and reflection is a process to accomplish this very thing.
For this particular purpose, looping through an object's properties at run-time, you use the GetProperties() method that is available for each Type. In your case, the Type you want to "reflect" is A, so the type-specific version of GetProperties returns a list of the instance properties for that object.
When you ask .NET to return the properties of an object, you can also specify what's called a binding flag that tells .NET which properties to return - public properties, private properties, static properties - a myriad of combinations from about twenty different values in the BindingFlags enumeration. For the purposes of this illustration, BindingFlags.Public will suffice, assuming your A0-A999 properties are declared to be public. To expose even more properties, simply combine multiple BindingFlag values with a logical "or".
So, now armed with that information, all we need to do is create a class, declare its properties, and tell Reflection to enumerate the properties for us. Assuming your Class A exists with property names A0-A999 already defined, here's how you'd enumerate ones starting with "A":
// Assuming Class "A" exists, and we have an instance of "A" held in
// a variable ActualA...
using System.Reflection
// The GetProperties method returns an array of PropertyInfo objects...
PropertyInfo[] properties = typeof(ActualA).GetProperties(BindingFlags.Public);
// Now, just iterate through them.
foreach(PropertyInfo property in properties)
{
if (property.Name.StartsWith("A")){
// use .Name, .GetValue methods/props to get interesting info from each property.
Console.WriteLine("Property {0}={1}",property.Name,
property.GetValue(ActualA,null));
}
}
There you have it. That's C# version rather than VB, but I think the general concepts should translate fairly readily. I hope that helps!
This MSDN code sample illustrates how to iterate over a class's properties using reflection:
http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx#Y900
Create a VB.Net Console application, copy and paste this code into the Module1.vb file and run it.
Module Module1
Sub Main()
For Each prop In GetType(TestClass).GetProperties()
Console.WriteLine(prop.Name)
Next
Console.ReadKey(True)
End Sub
End Module
Public Class TestClass
Private _One As String = "1"
Public Property One() As String
Get
Return _One
End Get
Set(ByVal value As String)
_One = value
End Set
End Property
Private _Two As Integer = 2
Public Property Two() As Integer
Get
Return _Two
End Get
Set(ByVal value As Integer)
_Two = value
End Set
End Property
Private _Three As Double = 3.1415927
Public Property Three() As Double
Get
Return _Three
End Get
Set(ByVal value As Double)
_Three = value
End Set
End Property
Private _Four As Decimal = 4.4D
Public Property Four() As Decimal
Get
Return _Four
End Get
Set(ByVal value As Decimal)
_Four = value
End Set
End Property
End Class
My program has decided to assign pointers instead of make copies of an object, and I'm not sure why. I have something like this:
Public Class Foo
Private myFooData As New List(Of FooData)
Public Sub New(ByVal newFooData As List(Of FooData))
myFooData = newFooData
End Sub
Public Property FooValues() As List(Of FooData)
Get
Return myFooData
End Get
Set(ByVal value As List(Of FooData))
myFooData = value
End Set
End Property
End Class
And it's used like this:
Public Sub Dosomething()
Dim mainFoo as new Foo
For x = 1 to 10
mainFoo.FooValues(x) = New FooData
Next
Dim originalFoo as new Foo
originalFoo.FooValues = mainFoo.FooValues.Take(3).ToList
Dim newFoo as new Foo
newFoo.FooValues = originalFoo.FooValues
newFoo.FooValues(1) += 1
End Sub
Very simplified, but basically what I'm doing. So for some reason today when I change item in newFoo.FooValues, originalFoo.FooValues also changes, and mainFoo does not. I've tried assigning the entire objects as well and I get the same results. Any ideas why this may be happening and how to fix it?
This is how assignment in .Net is supposed to work.
When you called .ToList() in the middle of your second snippet, your code iterates over the set and makes copies into a whole new list. This is why your mainFoo object is "protected" — you created a new instance. If the FooData items being copied are themselves references to objects (hint: they probably are), then only the references are copied. The only exceptions are for strings and value types (primitives and structures), or if you code it by hand.
It's usually a good idea for List properties to make the property readonly:
Public Property FooValues() As List(Of FooData)
Get
Return myFooData
End Get
End Property
This will still let you manipulate the list to your heart's content, but prevents you from completely switching a list instance out from under the class. The same is true for other complex types exposed from a class as a property.
The one thing you don't want to do is change this to be a Structure instead of a Class. This may seem to do what you want at first, but it will cause other problems for you later.
When using Foo.FooValues, you assign references to the List(Of FooData) internally used in your Foos - and no values!
Let's consider the steps
Dim mainFoo as new Foo
mainFoo now has its own backing list.
originalFoo.FooValues = mainFoo.FooValues.Take(3).ToList
originalFoo gets assigned a new backing list storing some of the values of mainFoo.
newFoo.FooValues = originalFoo.FooValues
newFoo used to have a backing list of its own, but now, it uses the one originalFoo uses. The exactly same one (by pointer) and no copy.
Thus changing newFoo will change originalFoo but not mainFoo which has its own list.
Since I don't know what you're trying to achieve after all, I cannot tell how to fix the code, but as you see, it's never a good idea to make some backing list accessible i.e. assignable.
Thus I'd advice to keep the list immutable and private, just giving indexing access.
OK, I've been working on something for a while now, using reflection to accomplish a lot of what I need to do, but I've hit a bit of a stumbling block...
I'm trying to use reflection to populate the properties of an array of a child property... not sure that's clear, so it's probably best explained in code:
Parent Class:
Public Class parent
Private _child As childObject()
Public Property child As childObject()
Get
Return _child
End Get
Set(ByVal value As child())
_child = value
End Set
End Property
End Class
Child Class:
Public Class childObject
Private _name As String
Public Property name As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Private _descr As String
Public Property descr As String
Get
Return _descr
End Get
Set(ByVal value As String)
_descr = value
End Set
End Property
End Class
So, using reflection, I'm trying to set the values of the array of child objects through the parent object...
I've tried several methods... the following is pretty much what I've got at the moment (I've added sample data just to keep things simple):
Dim Results(1) As String
Results(0) = "1,2"
Results(1) = "2,3"
Dim parent As New parent
Dim child As childObject() = New childObject() {}
Dim PropInfo As PropertyInfo() = child.GetType().GetProperties()
Dim i As Integer = 0
For Each res As String In Results
Dim ResultSet As String() = res.Split(",")
ReDim child(i)
Dim j As Integer = 0
For Each PropItem As PropertyInfo In PropInfo
PropItem.SetValue(child, ResultSet(j), Nothing)
j += 1
Next
i += 1
Next
parent.child = child
This fails miserably on PropItem.SetValue with ArgumentException: Property set method not found.
Anyone have any ideas?
#Jon :-
Thanks, I think I've gotten a little further, by creating individual child objects, and then assigning them to an array... The issue is now trying to get that array assigned to the parent object (using reflection).
It shouldn't be difficult, but I think the problem comes because I don't necessarily know the parent/child types. I'm using reflection to determine which parent/child is being passed in. The parent always has only one property, which is an array of the child object. When I try assigning the child array to the parent object, I get a invalid cast exception saying it can't convert Object[] to .
EDIT:
Basically, what I have now is:
Dim PropChildInfo As PropertyInfo() = ResponseObject.GetType().GetProperties()
For Each PropItem As PropertyInfo In PropChildInfo
PropItem.SetValue(ResponseObject, ResponseChildren, Nothing)
Next
ResponseObject is an instance of the parent Class, and ResponseChildren is an array of the childObject Class.
This fails with:
Object of type 'System.Object[]' cannot be converted to type 'childObject[]'.
Firstly I'd get rid of the array part of the equation - I can't see how that's relevant. Try to write code to set the values for a single child.
Secondly, it seems that you're relying on the results of GetProperties being in the desired order - you shouldn't. There's no guarantee as to what order the properties will be returned in. You should know what order you want based on the string you're splitting, and fetch the properties by name.
Thirdly, I suspect the problem is that you've got some read-only properties as well as writeable ones. I suggest you whip up a short console app to check this out, logging what properties you're trying to set before you set it.
If this doesn't help, please post a short but complete console app which demonstrates the problem, and I'm sure we'll be able to fix it.
EDIT: Okay, if you're now stuck just on the array part, I suggest you show a short but complete example of that instead. I suspect the problem is that you've created an array of the wrong type. You can use Array.CreateInstance to create the right kind of array, which should be valid when you then set the property.
There are libraries available to make it easier (and faster) to work with reflection. For instance, Fasterflect allows you to write the following:
parent.Property("child").GetElement(index).SetFieldValue("Name",name);
This will retrieve the property called "child" on the object "parent". Since we expect it to be an array we'll grab the element at position "index" (a single child instance) and set its Name property to "name".
Disclaimer: I am involved in said project as a contributor.