EF6 - add objects to tables dynamicaly - vb.net

I'm new around here (I know this site for long but it's my first time actually asking somehting).
Components that I'm using: - EF6, Devexpress XtraGrid
Ok... so, what I want is to kind of do this,
I have 1 form with multiple tables, and which I will have to be able to add and delete from each's NavigationBar.
I know how to do it, I just need a way to skip the select case.
Here's some code,
Private Sub ListChanged(sender As Object, e As System.ComponentModel.ListChangedEventArgs)
If e.ListChangedType = System.ComponentModel.ListChangedType.ItemAdded Then
Dim bList As BindingList(Of Object) = TryCast(sender, BindingList(Of Object))
Dim m As Object = bList.LastOrDefault()
If m IsNot Nothing Then
Select Case _curentPageIndex
Case 0 : db.GESTARM.Add(m)
Case 1 : 'Other table add
Case 2 : 'Other table add
End Select
End If
End If
End Sub
What I want to do with that is kind of this:
Private Sub ListChanged(sender As Object, e As System.ComponentModel.ListChangedEventArgs)
If e.ListChangedType = System.ComponentModel.ListChangedType.ItemAdded Then
Dim bList As BindingList(Of Object) = TryCast(sender, BindingList(Of Object))
Dim m As Object = bList.LastOrDefault()
'somehow get the table (type) of the entry through the m object
If m IsNot Nothing Then
db.<Table>.Add(m)
End If
End If
End Sub
So instead of writing every add for each case, I just had to do something like that.
Is it possible or am I going to stick with the select case?
Thanks in advance, and sorry if my english is bad (I'm not native).
EDIT 1:
as Mark mentioned in a comment we could use this in C#
but in VB it doesn't work...
Public Class GenericRepository(Of T)
Implements IDisposable
Friend context As GestProGest1Entities
Friend dbSet As Entity.DbSet(Of T) ' Gives error on T "Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'TEntity'"
Public Sub Dispose() Implements IDisposable.Dispose
If context IsNot Nothing Then
context.Dispose()
context = Nothing
End If
End Sub
Public Sub New(context As GestProGest1Entities)
Me.context = context
Me.dbSet = context.Set(Of T)() ' Gives error on T "Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'TEntity'"
End Sub
Public Overridable Sub Insert(entity As T)
dbSet.Add(entity)
context.SaveChanges()
End Sub
End Class
Any ideas how to do this in VB?
EDIT 2:
Ok, so I got it working like this
Public Class GenericRepository(Of T As Class)
now my problem is how to get the type from the object
Private Sub ListChanged(sender As Object, e As System.ComponentModel.ListChangedEventArgs)
If e.ListChangedType = System.ComponentModel.ListChangedType.ItemAdded Then
Dim bList As BindingList(Of Object) = TryCast(sender, BindingList(Of Object))
Dim m As Object = bList.LastOrDefault()
Dim myType As Type = m.GetType()
Dim table As New GenericRepository(Of myType)(db) 'Doesn't accept myType here...
table.Insert(m)
End If
End Sub

With Mark's help I finally got this working.
Private Sub ListChanged(sender As Object, e As System.ComponentModel.ListChangedEventArgs)
If e.ListChangedType = System.ComponentModel.ListChangedType.ItemAdded Then
Dim m As Object = sender(sender.count - 1)
db.Set(m.GetType()).Add(m)
End If
End Sub
Thanks for everyone's help!

Related

How to return specific properties of a custom vb.net object

I'm trying to generate a list of all of the TableName and FieldName properties for a custom object type called LxTextBox. I've gotten as far as generating a list of all of the LxTextBox names on my form, but I can't figure out a way to call the properties of the custom object... I've been looking into System.Reflection, but I haven't ever used it. Additionally, I'm returning the list to a RichTextBox while I'm testing this out, but ultimately, I need to return each objects properties as a data row. Example:
ObjectName Table Field
---------------------------------------
LxTextBox23 SomeTbl SomeFld
Here's my code to return the list - updated based on #OneFineDay...
Imports System.Collections.Generic
Imports Application.UDF.Controls
Public Class MeasurementsControl
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim textBoxList As New List(Of Control)
Dim customTbs = GetAllControls(Me)
Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To customTbs.Count - 1
sb.Append(customTbs.Item(index).TableName & "." & customTbs.Item(Index).FieldName & System.Environment.NewLine)
Next
RichTextBox1.Text = sb.ToString
End Sub
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
If searchWithin.HasChildren Then
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
For Each ctrl As Control In searchWithin.Controls
returnList.AddRange(GetAllControls(ctrl))
Next
End If
Return returnList
End Function
End Class
I made the changes suggested and I'm throwing an error: OfType is not a member of System.Windows.Forms.Control.ControlCollection
FYI - Adding Imports System.Linq did not fix the error.
You are boxing it into a Control the object from where it derives, where you're custom properties cannot be found. You can cast it right from the control collection.
Dim customTbs = GetAllControls(Me)
'recursive function
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
If searchWithin.HasChildren Then
For Each ctrl As Control In searchWithin.Controls
Dim ctrls = GetAllControls(ctrl)
If Not ctrls Is Nothing Then returnList.AddRange(ctrls)
Next
End If
Return returnList
End Function

Passing parameters between two forms in VB.Net

I currently have about 5 forms in my application. I'm building a 6th form - frmSummary however, I'd like to be able to access it from all forms. in frmSummary I am planning to add a DataGridView, where I'll be displaying data related to that form. I'm thinking that I should either create a global variable such as
dim FrmName as String
In each form I would have a cmdSummary button so that On click_event, I would do something like
frmName ="CustomerInfo"
Currently the way my application is set up is that I hve a mdiForm and within it, each form is a child so on opening new forms I do something like...
Private Sub cmdSummary_Click(sender As Object, e As EventArgs) Handles cmdSummary.Click
Dim NewMDIChild As New frmClientEligibilityReferral()
frmName = "CustomerInfo" --since this will be comeing from frmCustomerInfo
NewMDIChild.MdiParent = MDIform1
NewMDIChild.Show()
MDIForm1.Show()
End Sub
So I do something like that on opening my new form. My question is how can I pass the parameter to my form frmSummary....here's currently what I'm trying to accomplish....
Private Sub FrmSummary_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.MdiParent = MDIForm1
InitializeComponent()
'Here I want to call a function to load the datagridView(with g_frmName)see below...
call LoadDataGrid(frmName)
End Sub
Is something like that a smart idea? Or should I/Can I directly call the function from the previous form?
Just trying to see if I'm on the right track, if not, how can i do it in a sound way?
If there is only one frmSummary, you could make it a singleton.
In frmSummary, put the following code:
Private Shared _instance As frmSummary
Private Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
Public Shared Function GetInstance() As frmSummary
If _instance Is Nothing Then
_instance = New frmSummary()
End If
Return _instance
End Function
Public Sub PutDataInGrid(data As Object)
Me.DataGridView1.' put data in it
End Sub
And you would access it from other forms like this
Dim myFrmSummary = frmSummary.GetInstance()
myFrmSummary.PutDataInGrid(myData)
If I understand the question correctly....
You can just set the required parameters in the New declaration sub (Where InitializeComponent() is supposed to be). On your form, declare variables and set one to each of the parameter values, and set up your form this way..
An example might be;
Public Class frmSummary
Dim var1 as String = ""
Dim var2 as Boolean = True
Public Sub New(ByVal parameter1 as String, ByVal parameter2 As Boolean)
var1 = parameter1
var2 = parameter2
InitializeComponent()
End Sub
Private Sub frmSummary_Load(sender as Object, e As EventArgs) Handles MyBase.Load
If var1 = "This String" Then
If var2 = False Then
sql = "SELECT * FROM myTable"
' Rest of your code to get the DGV data
DataGridView1.DataSource = Dt
Else
End If
End If
End Sub
Again, I may have misunderstood the question, so apologies if that is the case.

Modify a structure field containing combobox when SelectedIndex event fires

I am trying to have a generic widget composed of a label and a value. The value is set by a combobox. Here is the structure:
Structure tParam
Dim label As Label
Dim comboBox As ComboBox
Dim valueX As String
End Structure
Dim parameter1 As tParam
I'd like to modify the valueX as the SelectedIndexChanged event is fired.
For now I have set
parameter1.label.text = "Id"
parameter1.comboBox.Tag = parameter1 ' the struct itself
AddHandler parameter1.comboBox.SelectedIndexChanged, AddressOf updateParam
and in the handler
Private Sub updateParam(sender As Object, e As System.EventArgs)
Dim parameterX As tParam = sender.Tag
With parameterX
Select Case .label.Text
Case "Id"
parameter1.valueX = .comboBox.SelectedIndex
End Select
End Sub
The problem is that I have a lot (>50) parameters of type tParam and I like not to check every parameter name with the select case.
Note that I am calling directly parameter1 in the handler, because parameterX (=sender.Tag) is read-only, as any update to parameterX is local.
I cant quite tell what you are trying to do, but tStruct.ComboBox.Tag = Me seems a convoluted way to track your widgets. Using a class, you could internalize and simplify some of what it seems you are trying to do:
Public Class CBOWidgetItem
Private WithEvents myCBO As ComboBox
Private myLbl As Label
Public Property Name As String
Public Property Value As String
Public Sub New(n As String, cbo As ComboBox, lbl As Label)
Name = n
myCBO = cbo
myLbl = lbl
End Sub
Private Sub myCBO_SelectedIndexChanged(sender As Object,
e As EventArgs) Handles myCBO.SelectedIndexChanged
Value = myCBO.SelectedIndex.ToString
End Sub
Public Overrides Function ToString() As String
Return Name
End Function
End Class
The widget is able to handle the Value change itself (again, I dont quite know what you are up to). You might have other wrapper props to expose certain info the widget is managing:
Public ReadOnly Property LabelText As String
Get
If myLbl IsNot Nothing Then
Return myLbl.Text
Else
Return ""
End If
End Get
End Property
To use it:
' something to store them in:
Private widgets As List(Of CBOWidgetItem)
...
widgets = New List(Of CBOWidgetItem)
' long form
Dim temp As New CBOWidgetItem("ID", ComboBox1, Label1)
widgets.Add(temp)
' short form:
widgets.Add(New CBOWidgetItem("foo", ComboBox2, Label2))
Elsewhere if you need to find one of these guys:
Dim find = "ID"
Dim specificItem = widgets.Where(Function(s) s.Name = find).FirstOrDefault
If specificItem IsNot Nothing Then
Console.WriteLine(specificItem.Name)
End If
Alternatively, you could use a Dictionary(Of String, CBOWidgetItem) and get them back by name.

Showing classes to user and instantiating selected type

I'm starting to learn about Reflection in VB.NET, and I have a little example problem I'm working on to understand some concepts.
So I have one interface implemented by three classes:
Public Interface IVehicle
Sub SayType()
End Interface
Public Class Bike
Implements IVehicle
Public Sub SayType() Implements IVehicle.SayType
MsgBox("I'm a bike")
End Sub
End Class
Public Class Car
Implements IVehicle
Public Sub SayType() Implements IVehicle.SayType
MsgBox("I'm a car")
End Sub
End Class
Public Class Plane
Implements IVehicle
Public Sub SayType() Implements IVehicle.SayType
MsgBox("I'm a plane")
End Sub
End Class
I would like the user to select one type of vehicle of all the vehicles available, instantiate one object of this type and call its method "SayType".
So, with this situation, I have 2 questions
The 1st one: I have thought about filling one ComboBox control with all the classes which implement the interface IVehicle. I have searched how to do so with reflection, and I've came up with this solution:
Private Function ObtainVehicleTypes() As IEnumerable(Of Type)
Dim types As IEnumerable(Of Type) = _
Reflection.Assembly.GetExecutingAssembly.GetTypes.Where(Function(t) _
t.GetInterface("IVehicle") IsNot Nothing)
Return types
End Function
With those types, I fill the ComboBox like this, which also works fine:
Private Sub AddTypesOfVehicles()
Dim types As IEnumerable(Of Type) = ObtainVehicleTypes()
For Each t As Type In types
ComboBox1.Items.Add(t.Name)
Next
End Sub
The problem is that, when I try to retrieve the item selected by the user and obtain the type asociated like shown below, I get Nothing, since the String doesn't contain the AssemblyName, only the Class name:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim type As Type = TryCast(ComboBox1.SelectedItem, Type) 'Here I get Nothing
Dim v As IVehicle = TryCast(Activator.CreateInstance(type), IVehicle)
v.SayType()
End Sub
I have also tried to to add this to the combobox:
For Each t As Type In types
ComboBox1.Items.Add(t) 'Adding the type, not only its name.
Next
But then it displays the AssemblyName to the user, which I want to avoid.
So, the question is... how would you do to show the classes to the user and the retrieve them correctly to instantiate an object of the chosen class?
The 2nd question: Do you consider this as a good approach? Would you suggest something simpler?
Thanks!
I do not understand the need of the method SayType on the interface. All types implements the GetType method which will return the info you'll need.
Dim vehicles As IVehicle() = New IVehicle() {New Bike(), New Car(), New Plane()}
For Each vehicle As IVehicle In vehicles
MsgBox(String.Format("I'm a {0}", vehicle.GetType().Name.ToLower()))
Next
'This will produce:
'------------------
'I'm a bike
'I'm a car
'I'm a plane
'------------------
This is how you could populate the combobox:
Dim t = GetType(IVehicle)
Dim list As List(Of Type) = Assembly.GetExecutingAssembly().GetTypes().Where(Function(x As Type) ((x <> t) AndAlso t.IsAssignableFrom(x))).ToList()
Me.ComboBox1.DataSource = list
Me.ComboBox1.DisplayMember = "Name"
And to retrieve the type:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles saveButton.Click
If (Me.ComboBox1.SelectedIndex <> -1) Then
Dim t As Type = TryCast(Me.ComboBox1.SelectedItem, Type)
If (Not t Is Nothing) Then
MsgBox(t.FullName)
End If
End If
End Sub
Edit
A more real-world example of an IVehicle interface would be something like:
Public Interface IVehicle
ReadOnly Property Manufacturer() As String
ReadOnly Property Model() As String
Property Price() As Decimal
End Interface
combobx problem its Excellent answered by #Bjørn-Roger Kringsjå.
Here are additional improvements:
ObtainVehicleTypes:
Private Function ObtainVehicleTypes() As IEnumerable(Of Type)
Dim IVehicleType = GetType(IVehicle)
Dim types As IEnumerable(Of Type) = _
Reflection.Assembly.GetExecutingAssembly.GetTypes.Where(
Function(t) IVehicleType.IsAssignableFrom(t) AndAlso t.IsClass = True)
Return types
End Function
Private Sub AddTypesOfVehicles()
Dim types As IEnumerable(Of Type) = ObtainVehicleTypes().ToArray()
ComboBox1.DisplayMember = "Name"
ComboBox1.DataSource = types
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim type As Type = TryCast(ComboBox1.SelectedItem, Type)
Dim v As IVehicle = TryCast(Activator.CreateInstance(type), IVehicle)
v.SayType()
End Sub

Using Generic List(Of Form), Trouble gathering Object's Name Property

I have been very interested as of late in interfaces and the ability to further customize them beyond using them in their default state.
I have been researching IList(of T) specifically. The advantages of using generic lists as opposed to ArrayLists has astounded me. Here is a picture of a test. This is the site that goes into further explanation about the Test.
So, naturally I wanted to experiment. When I first iterate through the list with the ForNext method the code works fine. The second time I can't access the name of the Form in the list because it is disposed. Anyone have any insight how I can access the forms properties in the list.
Public Class frmMain
Dim Cabinet As List(Of Form) = New List(Of Form)
Dim FormA As New Form1
Dim FormB As New Form2
Dim FormC As New Form3
Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles _Me.Load
Cabinet.Add(FormA)
Cabinet.Add(FormB)
Cabinet.Add(FormC)
End Sub
Sub displayForm(ByVal aForm As Form)
Dim myFormName As String = ""
Stopwatch.Start()
If aForm.IsDisposed = False Then
aForm.Show()
Else
myFormName = aForm.(How do I access this objects Name?)
aForm = New Form '<----- I would rather simply use aForm = New(aForm)
aForm.Name = myFormName
aForm.Show()
End If
Stopwatch.Stop()
Dim RealResult As Decimal = (Stopwatch.ElapsedMilliseconds / 1000)
Debug.WriteLine(RealResult)
Stopwatch.Reset()
End Sub
Private Sub btnForEach_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnForEach.Click
'Dim instance as List
'Dim action as Action(of T)
'instance.ForEach(action)
'action = delegate to a method that performs an action on the object passeed to it
Cabinet.ForEach(AddressOf displayForm)
End Sub
I really don't understand why if VB knows that this is a Generic list, which means it is knowledgable of the list's type, and the objects are all constrained to be forms; why I can't call a constructor on an item in the list. Ex. aForm = New aForm or aForm = New Cabinet.aForm
Tear this one open for me somebody. Thanks.
You can't construct a new instance of "aForm" because its isn't a type, it is an instance of type Form.
If you wanted to prevent the ObjectDisposedException, you could hide the form instead of closing it. Place the following code in each forms code behind:
Public Class Form1
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Dim form = CType(sender, Form)
form.Visible = False
e.Cancel = True
End Sub
End Class
This is a bit hacky, however, but then you wouldn't need the code in the Else block.
Edit
You could try this instead:
Private Sub displayForm(ByVal aForm As Form)
Dim indexOfCab As Integer = Cabinet.IndexOf(aForm)
If indexOfCab <> -1 Then
If aForm.IsDisposed Then
aForm = CreateForm(aForm.GetType())
Cabinet(indexOfCab) = aForm
End If
aForm.Show()
End If
End Sub
Private Shared Function CreateForm(formType As Type) As Form
Return CType(Activator.CreateInstance(formType), Form)
End Function
You wouldn't need that big Select statement.
This is the only way I have been able to get it to work. I feel it is extremely inefficient however, and hope someone can set me on a path to a better way to do this. The below is what I'm trying to achieve.
Sub displayForm(ByVal aForm As Form)
Dim myFormName As String = ""
If Cabinet.Contains(aForm) Then
Dim indexOfCab As Integer = Cabinet.IndexOf(aForm)
Dim ObjForm As Form = Cabinet.Item(indexOfCab)
If aForm.IsDisposed Then
Select Case indexOfCab
Case 0
aForm = Nothing
aForm = New Form1
Cabinet.Item(indexOfCab) = aForm
Cabinet.Item(indexOfCab).Show()
Case 1
aForm = Nothing
aForm = New Form2
Cabinet.Item(indexOfCab) = aForm
aForm.Show()
Case 2
aForm = Nothing
aForm = New Form3
Cabinet.Item(indexOfCab) = aForm
Cabinet.Item(indexOfCab).Show()
End Select
Else
Cabinet.Item(indexOfCab).Show()
End If
End If
End Sub