Structure as UserControl property - vb.net

This question is with VB2008 Express.
I'm making a usercontrol which uses a structured property. Both the control and the overall project have an identical structure. The problem is that within the main project, attempts to assign a place to this property results in: "Reference to a non-shared member requires an object reference."
I have no clue what that means, nor how to deal with it. Microsoft's help babbles something like: "You are referencing a non shared member, so an object reference will be required."
Well gee Microsoft, I read the error description so no shiznit... But what's that mean? (I came from VB6 and I'm self-taught by example from there so please go easy on me.)
Of course I could assign each individual piece of the structure as its own property, such as "Street" "City", etc. but there are reasons I prefer to do it in one step, as it is validated all at once by the usercontrol upon assignment.
Any help getting my usercontrol and my main project to pass a "place" to each other?
Public Structure Place
Public PlaceName As String
Public Street As String
Public Apt As String
Public City As String
Public State As String
Public Zip As String
Public VerifiedStatus As Integer
Public Lat As Single
Public Lng As Single
End Structure
Public Property CurrentPlace() As Place
Get
Dim ThisPlace As New Place
ThisPlace.Street = Trim(Me.txtStreet.Text)
ThisPlace.Apt = Trim(txtAptNo.Text)
ThisPlace.City = Trim(txtCity.Text)
ThisPlace.State = Trim(lblState.Text)
ThisPlace.Zip = Trim(txtZip.Text)
ThisPlace.Lat = MyLat
ThisPlace.Lng = MyLng
ThisPlace.PlaceName = ""
'This control doesn't take placenames but they exist in the structure.
ThisPlace.VerifiedStatus = MyVerifiedStatus
Return ThisPlace
End Get
Set(ByVal value As Place)
AsLoadedApt = Trim(value.Apt)
AsLoadedCity = Trim(value.City)
AsLoadedLat = value.Lat
AsLoadedLng = value.Lng
AsLoadedState = Trim(value.State)
AsLoadedStreet = Trim(value.Street)
AsLoadedVerifiedStatus = value.VerifiedStatus
AsLoadedZip = Trim(value.Zip)
txtStreet.Text = AsLoadedStreet
txtAptNo.Text = AsLoadedApt
txtCity.Text = AsLoadedCity
lblState.Text = AsLoadedState
txtZip.Text = AsLoadedState
MyVerifiedStatus = AsLoadedVerifiedStatus
MyLat = AsLoadedLat
MyLng = AsLoadedLng
Call ShowStatus()
End Set
End Property

With your structure inside the control and the usercontrol file as part of the project, the structure will be exposed as a type by qualifying it as part of the usercontrol:
Dim NewPlace As New UserControl1.Place
Now since you're using the same structure, the NewPlace object can be used to set the CurrentPlace property
With NewPlace
.Apt = "Apt"
.City = "City"
.Lat = 0
.Lng = 0
.State = "State"
.Street = "Street"
.Zip = "Zip"
End With
UserControl11.CurrentPlace = NewPlace
If it's part of a different project in the same solution add the qualification for the project as well.

Related

dim Pull data from lower variable to higher

I have
Dim m_LedgerList As New Ejm.Financial.Entities.LedgerList
m_LedgerList = My.StaticData.LedgerList
m_LedgerList.Filter = "LedgerfunctionID = 2"
but the filter i put on m_LedgerList.Filter pull trough to My.StaticData.LedgerList
Any idees how to stop the filter to go up?
This line
m_LedgerList = My.StaticData.LedgerList
makes your variable m_LedgerList reference the same data referenced by the variable My.StaticData.LedgerList. This is not a copy, this is merely two variables that look at the same data in memory. Thus any action that you perform on the m_LedgerList variable acts on the same data seen by the My.StaticData.LedgerList.
If you want to have a different set of data then you need to duplicate the original data in a new memory location. This could be done inside the LedgerList class with something like this
Public Class LedgerList
Public Function Duplicate() As LedgerList
' This create a new memory area for a new LedgerList
Dim result = new LedgerList()
' Code to duplicate and add the elements in this
' class to the new list
return result
End Function
End Class
Now you can go with
m_LedgerList = My.StaticData.LedgerList.Duplicate()
m_LedgerList.Filter = "LedgerfunctionID = 2"
You can implement ICloneable as follows:
Class LedgerList
Implements ICloneable
Property Property1 As String
Property Filter As String
Public Function Clone() As Object Implements ICloneable.Clone
Dim myclone As New LedgerList
myclone.Property1 = Me.Property1
myclone.Filter = Me.Filter
Return myclone
End Function
End Class
Then to make a copy of your object:
Dim m_LedgerList As Ejm.Financial.Entities.LedgerList = DirectCast(My.StaticData.LedgerList.Clone, Ejm.Financial.Entities.LedgerList)
m_LedgerList.Filter = "LedgerfunctionID = 2"

Using for each to iterate class properties

I have this class
Public Class TExperience
Public ID As Integer
Public CompanyName As String
Public Country As String
Public Years As Integer
Public Months As Integer
Public JobTitle As String
Public Function Arr() As String()
Return {"ID", "CompanyName", "Country", "Years", "Months", "JobTitle"}
End Function
End Class
and this is how it work
Public Function Set()
Dim D As New TExperience
D.CompanyName = "Any Data"
D.Country = "Any Data"
D.ID = "Any Data"
D.JobTitle = "Any Data"
D.Months = "Any Data"
End Function
I need to exchange this by "For Each....." in class
D.CompanyName = "Any Data"
D.Country = "Any Data"
D.ID = "Any Data"
D.JobTitle = "Any Data"
D.Months = "Any Data"
How to do this?
Yea, this is possible if...
Note: you posted class with public fields, e.g. Public CompanyName As String. While it is usually good idea to use properties - Public Property CompanyName As String. The code below is for class properties but you can substitute PropertyInfo for FieldInfo. However, it is recommended to use properties to expose class values.
If you're looking for yes/no answer to question, can setting properties via for-each happen?, the answer is yes. But more questions about your question should arise. When, why, what for..., etc.
For example, we have a situation that specific types have common properties with other types, sometimes even deriving from those types and we need to convert them. One way was to use a "Decorator" pattern. But here is what we've done to hydrate properties (disclaimer - code was converted from c# via converter and I added some tings like "as type", I am not sure it is 100% correct)
''' Everything "S" - source, "D" - destination. "T" - type
Public Shared Function Hydrate(Of TS, TD)(source As TS, destination As TD) As TD
Dim sPI() As PropertyInfo =
source.[GetType]().GetProperties(BindingFlags.[Public] Or BindingFlags.Instance)
Dim dPI() As PropertyInfo =
destination.[GetType]().GetProperties(BindingFlags.[Public] Or BindingFlags.Instance)
For Each s As PropertyInfo In sPI
Dim d As PropertyInfo =
dPI.FirstOrDefault(Function(pi) pi.Name = s.Name AndAlso pi.PropertyType = s.PropertyType)
If d IsNot Nothing Then
d.SetValue(destination, s.GetValue(source, Nothing), Nothing)
End If
Next
Return destination
End Function
So, here we go. In this example properties iterated and when their names and data types match exactly, they are populated. But other than hydration of similar models, what is the use of it? I mean, this is what happens internally when JSON being deserialized, etc. But in everyday programming you don't do anything like this.
What you're asking for is not possible. As you have been told, For Each is for performing the same action for each item in an enumerable list. The properties of an object are not an enumerable list, therefore For Each is not applicable in this case. The way to set multiple property values is just as you are doing now.
That said, when setting multiple properties on a just-created object, you can use object initialiser syntax, e.g.
Dim D As New TExperience With
{
.CompanyName = "Any Data",
.Country = "Any Data",
.ID = "Any Data",
.JobTitle = "Any Data",
.Months = "Any Data"
}
Obviously not what you were asking for but still an improvement on what you're doing now. Alternatively, if the object requires all that data, it is preferable to declare a constructor with appropriate parameters, that way, the call MUST provide the data. You've still got to set each property individually inside the constructor though.

How to bind the selected member of a ComboBox?

I have a ComboBox that is populated with objects of type ProfileName
Private Class ProfileName
Public Property Name As String
Public Property File As String
Public Property ProductVersion As String
End Class
These items are created added to the combo box after a deserialising a bunch of files and copying some of the values from the resulting objects:
pcb.DisplayMember = "Name"
For Each F As FileInfo In ProfileFiles
Dim Reader As StreamReader = F.OpenText()
Dim Serialize As Serializer = New Serializer()
Dim SerializedData As String = Reader.ReadToEnd()
Dim P As Profile = Serialize.DesearializeObject(Of Profile)(SerializedData)
If P.Type = Profile.ProfileType.Product Then
Dim PN As ProfileName = New ProfileName()
PN.File = F.Name
PN.ProductVersion = P.ProductVersion
PN.Name = P.ProductName & " - " & P.ProductVersion
pcb.Items.Add(PN)
End If
Reader.Close()
Next
Then if a user opens one of these files, the file will be again deserialised resulting in a Profile object with a 'ProductName' property that should match one of the items already on the ComboBox items list, so I'd like for the ComboBox to show that as the selected item.
i.e.
-On form load the ComboBox is populated with all possible product names.
-When a profile file is opened the product that the profile uses is automatically selected in the ComboBox.
I've been playing with
ProductComboBox.DataBindings.Add("SelectedValue", CurrentProfile, "ProductName")
and permutations thereof, but can't seem to get it right.
You cant mix and match - put objects into the items collection and use the data binding methods/elements. Databinding basics:
Public Class Profile
Public Property Name As String
Public Property File As String
Public Property ProductVersion As String
Public Overrides Function ToString() As String
Return String.Format("{0} ({1})", Name, ProductVersion)
End Function
End Class
The ToString() controls what will be displayed when you cant specify the property to display. Note, these should be properties becaus mere Fields will be treated differently.
Then a container for them. This will be the DataSource for the cbo.
Private Profiles As List(Of Profile)
...
' create instance of list and populate from where ever:
Profiles = New List(Of Profile)
Profiles.Add(New Profile With {.Name = "Default", .File = "foo",
.ProductVersion = "1.0"})
Profiles.Add(New Profile With {.Name = "Ziggy", .File = "bat",
.ProductVersion = "1.9.8"})
Profiles.Add(New Profile With {.Name = "Zoey", .File = "bar",
.ProductVersion = "1.4.1"})
Rather than putting the Profile objects into the Items collection, bind the control to the List:
cboP.DataSource = Profiles
cboP.DisplayMember = "Name"
If you omit the property to display, ToString() will be shown (or WindowsApp1.Profile if you did not override it). Note: When using a DataSource you no longer add or delete from the control's Items collection - it will yell at you. Instead manage the underlying source, your List(Of Profile) in this case.
To change the selection, for example to the one for "Ziggy":
Dim n As Int32 = Profiles.FindIndex(Function(f) f.Name = "Ziggy")
If n > -1 Then
cboP.SelectedIndex = n
End If
You can also set SelectedItem after you find the Profile instead, but I tend to use index. Even though the list is a new actor, serializing the entire thing is easy:
' serializing the List acts on all the profiles in it
Dim json = JsonConvert.SerializeObject(Profiles)
File.WriteAllText("C:\Temp\Profiles.json", json)
Read it back:
json = File.ReadAllText("C:\Temp\Profiles.json")
Dim newPs = JsonConvert.DeserializeObject(Of List(Of Profile))(json)
Its a bit simpler than looping thru a set of files. List(of T) has a full set of methods and extensions to remove, sort, find etc so you should gain functionality over the items collection or array.
Alternatively, you could keep the one file per structure, but add the deserialized Profile objects to a List(of Profile) rather than the Items collection.

Entity Framework 6 Navigation Property Collections

I am developing a database first application in VB.NET using entity framework version 6.1.1 and sql server 2008. I have some pure join tables that link many to many relationships between two tables. My entities are untracked.
Here are basic examples of the classes (generated from the EF tt files) that I am using:
Public Class Part
Public Property id As Long
Public Property name As String
Public Overridable Property CarModels As ICollection(Of CarModel) = New HashSet(Of CarModel)
End Class
Public Class CarModel
Public Property id As Long
Public Property name As String
Public Overridable Property Parts As ICollection(Of Part) = New HashSet(Of Part)
End Class
When I am updating fields for an entity, I set the values, and then include code like this:
obj.Name = "New Name"
context.Entry(obj).State = EntityState.Modified
context.SaveChanges
That will save the Name value to the database as I expect. My problem is trying to add a new CarModel to an existing part, or remove an existing CarModel from a part. I've tried several things but have not found a solution. Here's an example of my code:
Dim p As Part = context.Parts.where(Function(it) it.id.equals(1)).first 'Part I am working with
Dim c As CarModel = context.CarModels.where(Function(it) it.id.equals(1)).first 'Car Model I want to associate to the part
p.CarModels.Add(c) 'Add the Car Model to the part collection
context.Entry(p).State = EntityState.Modified
context.SaveChanges
No error is thrown. When I am debugging the CarModel is Added to the Part.CarModel collection. However, the changes are not committed to the database. If I add a NEW Part and use similar code it work, but I cannot add or remove from an existing collection and get it to commit to the database.
I haven't used VB in 6 years, so this might not be exactly right, but this will give you a general idea on how it works.
Dim p As Part = context.Parts.where(Function(it) it.id.equals(1)).first 'Part I am working with
Dim c As CarModel = context.CarModels.where(Function(it) it.id.equals(1)).first 'Car Model I want to associate to the part
Dim newPart As Part = New Part()
newPart.id = p.id
newPart.name = p.name
newPart.CarModels = c
context.Add(p)
context.SaveChanges()
I took a look at the context itself, and this line was in the constructor of the context:
Configuration.AutoDetectChangesEnabled = False
That is what was causing my particular problem. I read somewhere (after I found that line) that it is recommended to not turn set AutoDetectChangesEnabled to false unless there is a very long running process, and in that case to set it back to true after the process had completed. Removing that line from the context constructor solved my problem.

ByRef not carrying Through IN vb.net

The one calls form 2 as a dialog, and passed by ref a "pointer" to the base class(abstract).
//Form 1 calling form two. And Passing a ref object
Dim CreateForm As New frmCreate(Robot)
//Second Forms Overloaded New
Public Sub New(ByRef Robot As cRobot)
InitializeComponent()
thisRobot = Robot
End Sub
Select Case (cbType.SelectedIndex)
Case 0
lblOther.Text = "Bullet Proof Value"
Dim SecRobot = New cSecurityRobot
SecRobot.Name = txtName.Text
SecRobot.Temperature = nudTemp.Value
SecRobot.Threshold = nudThreshold.Value
SecRobot.BulletproofValue = nudOther.Value
thisRobot = SecRobot
Case 1
lblOther.Text = "Special Moves"
Dim SpRobot = New cSportsRobot
SpRobot.Name = txtName.Text
SpRobot.Temperature = nudTemp.Value
SpRobot.Threshold = nudThreshold.Value
SpRobot.SpecialMoves = nudOther.Value
thisRobot = SpRobot
Case 2
lblOther.Text = "Domestic Skills"
Dim SerRobot = New cServiceRobot
lblOther.Text = "Domestic Skills"
SerRobot.Name = txtName.Text
SerRobot.Temperature = nudTemp.Value
SerRobot.Threshold = nudThreshold.Value
SerRobot.DomesticSkills = nudOther.Value
thisRobot = SerRobot
Case Else
lblOther.Text = "Bullet Proof Value"
Dim SecRobot = New cSecurityRobot
SecRobot.Name = txtName.Text
SecRobot.Temperature = nudTemp.Value
SecRobot.Threshold = nudThreshold.Value
SecRobot.BulletproofValue = nudOther.Value
thisRobot = SecRobot
End Select
Form 2 assigns some values and terminates, but there is always a NULL Exception occurring
Let's take a look at your constructor:
Public Sub New(ByRef Robot As cRobot)
InitializeComponent()
thisRobot = Robot '<-- Problem is here
End Sub
On the line indicated above, you are making a copy of the reference, and so the ByRef no longer helps you.
Thinking about how to get around this problem, you might be able to do it by nesting the Robot inside another class:
Public Class RobotContainer
Public Property Robot As Robot
End Class
Pass a RobotContainer instance to your constructor in the normal (ByVal) way and keep a reference to that entire object in your class. Now, both your frmCreate type and the calling code have a reference to the same object. When you update the Robot property on that object, it will be updated for both locations.
But really, this whole design doesn't smell right. Normally I would suggest a method that return the created robot, rather than trying to assign it to an outside location directly, but I understand that working with Windows Forms controls this may not be option. To suggest a better solution we'd need to see a lot more of your code.
Hmm... looking back at this I wanted to do something to make the RobotContainer more useful:
Public Class ReferenceContainer(Of T)
Public Property Item As T
End Class
No, the "ByRef"-ness is only relevant for the method in which the parameter is declared. The value of the thisRobot variable is still just a reference value. Changing the value of that variable later on won't change the caller's variable.