Embed database in vb.net app - vb.net

I'm building a small VB.net app. I want to add a little database (about 5 columns, 20 records). I want to keep everything in a single exe. I think it's a bit overkill to add a 'full' database, so I'm looking for an alternative.
I could create a CSV file, and add it as a resource. Is this a good idea, or are there any other better alternatives?

Another option for such a small amount of data is to store it in ApplicationSettings. Your question implies you are using WinForms, so you can make use of the built-in features with just a small amount of work to store your own custom class.
Create a class to represent your data.
Wrap that class in a property of another class that inherits from ApplicationSettingsBase as a List(Of )
Manipulate this custom setting as needed and call Save() as needed.
Here is an example that binds to a DataGrid:
The class that represents your data:
Public Class Fruit
Public Property FruitName As String
Public Property FruitColor As String
Public Property FruitGrowsOn As String
End Class
The class that turns Fruit into a collection stored in application settings. Notice it inherits ApplicationSettingsBase. Also notice the attributes on the Fruits property that identify this as a user setting as opposed to an application setting (which cannot be modified by the user). The DefaultSettingAttribute makes sure the collection is instantiated so you don't get null reference exception until after the first time you add an item:
Imports System.Configuration
Public NotInheritable Class FruitCollection
Inherits ApplicationSettingsBase
<UserScopedSettingAttribute()>
<DefaultSettingValue("")>
Public Property Fruits() As List(Of Fruit)
Get
Fruits = Me("Fruits")
End Get
Set(ByVal value As List(Of Fruit))
Me("Fruits") = value
End Set
End Property
End Class
The Form definition. Retrieves the instance of your custom setting (FruitUserSettings), creates a binding source for a DataGridView, and provides a Save button to persist the changes made in the grid to Settings. Next time the user opens the form the changes will still be there provided they clicked the Save button:
Public Class Form1
Dim FruitUserSettings As FruitCollection
Dim GridBindingSrc As BindingSource
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FruitUserSettings = New FruitCollection()
GridBindingSrc = New BindingSource(FruitUserSettings, "Fruits")
DataGridView1.AutoGenerateColumns = True
DataGridView1.DataSource = GridBindingSrc
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
FruitUserSettings.Save()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
GridBindingSrc.Dispose()
End Sub
End Class
Note, you don't need a binding source or grid, these were just for demonstration. You can manipulate FruitUserSettings.Fruits like any other list in any way you want. As long as Save() is called on the settings you will retain the data.
You can download/clone the working sample here: https://github.com/crowcoder/CustomSetting

i would use XML file as little database, you can query it easily with linq (Language-Integrated Query). also there are built in library's that can help you handle you records and query's. of course that you can use access, excel (you can query excel with SQL) csv or txt file . also you can create a local data base file in visual studio

Related

How can I declare and initialize a VB.Net structure in a Form class to define a variable that contains all DataGridView columns in separate variables?

When I want to use a specific column index, I can use the column object define by Visual Studio designer
sAccount = grid.Rows(e.RowIndex).Cells.Item(Col_Account.Index).Value
where Col_Account object is defined automatically by Visual Studio in Form.Designer.vb file in following line
Friend WithEvents Col_Account As DataGridViewTextBoxColumn
The problem is that it is not very easy to find column's name defined manually using Visual Studio WinForm editor in a Form.
So I decide to create a structure in which I will put all columns defined in Designer Visual Studio as this
Public Structure GridColumns
Public Account = Col_Account
Public Communication = Col_Communication
Public Amount = Col_Amount
End Structure
and that I can use in Intellisense to find very quickly all column's name in typing
GridColumns.
When I compile this code, I receive following error code
BC30469: Reference to a non-shared member requires an object reference.
That is not working.
The only solution, that I have found is to define following structure
Public Structure GridColumns
Public Account As DataGridViewTextBoxColumn
Public Communication As DataGridViewTextBoxColumn
Public Amount As DataGridViewTextBoxColumn
End Structure
Public xColumns As GridColumns
and to initialize his members in Me.Load Form handle as this
Private Sub FrmSearch_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Handles Me.Load
With xColumns
.Account = Col_Account
.Amount = Col_Amount
.Communication = Col_Communication
End With
After compiling, I can now type xColumns. to see all possible column's names usable in my code.
Is there a simple solution to implement what I want ? A solution that mix declaration and initialisation in only one place ?
I work using Visual Studio 2022 and I use following solution
Public Structure GridColumns
Public Shared Account As DataGridViewTextBoxColumn = Frm.Col_Account
Public Shared Communication As DataGridViewTextBoxColumn = Frm.Col_Communication
Public Shared Amount As DataGridViewTextBoxColumn = Frm.Col_Amount
End Structure
Example of use in VB.Net code
Private Sub grid_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles grid.CellClick
If e.RowIndex < 0 Then
Exit Sub
End If
Select Case e.ColumnIndex
Case GridColumns.Edit.Index 'click on Image "PENCIL"
Dim sNoSeq = grid.Rows(e.RowIndex).Cells.Item(GridColumns.YearNoSeq.Index).Value
Dim sPersonalAccount = grid.Rows(e.RowIndex).Cells.Item(GridColumns.Account.Index).Value
nPayementGridIndex = e.RowIndex
Call frmMDI.LoadDetailForm(sNoSeq, sPersonalAccount)
With this solution, I can define multiple tabs in same Form that contains each one grid.
When I reference a specific column, I use
TabAccountColumns.Account.Index
or
TabCountryColumns.Name.Index
The list of alls columns of a grid are now in a specific variable that I can use instead of Frm variable in which all columns of all grids are mixed.
It is certainly more proper.

How to access data from calling object in vb.net

I have a Window-Form 'caller' in vb.net containing a datagridview with a small overview table of certain objects, each with its own ID in the first column. Now, if a row is double clicked, i want to show a dialog 'edit', where one can edit many details of that row which i do not want in the overview table.
My approach is as follows: In the caller form i wrote this to call 'edit':
Private Sub dgdata_dbclick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dg_data.CellMouseDoubleClick
Dim f_edit As New edit
f_edit.ShowDialog(Me)
End Sub
That works fine.
However, in the called Form "edit" i need to check, which ID was selected and load this data from the database to edit it. I can access some data from the calling form 'caller' using e.g.
MsgBox(CType(Me.Owner, caller).Text)
to show the window title of 'caller'. However, i want to extract the currently selected ID in the datagridview or at least some variabhle containing it. In the caller form, this could be easily done by evaluating
dg_data.Item(0, selectedRow).Value.ToString
but i cannot access any relevant information in 'caller'. I have a public class with some global variables there but i cannot access them as well.
Probably my strategy to solve this problem is not the most clever approach? Basically, i want to open a very detailed edit window when someone clicks on a line in an overviewtable but simultaniously blocking the rest of the application as long as the edit window is open.
Thanks!
The idea is to pass the data to the second form. When you create an instance of the second form (my class is called Form2, yours is called edit) with the New keyword the Sub New is called on Form2.
Private Sub OpenEditDialog()
Dim f_edit As New Form2(32) '32 is the number you retrieve from your DataGridView
f_edit.ShowDialog(Me)
f_edit.Dispose()
End Sub
You pass the ID to Form2 and set a variable at Form level. You can then use the variable anywhere in Form2.
Public Class Form2
Private ID As Long
Public Sub New(SelectedID As Long)
InitializeComponent()
ID = SelectedID
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MessageBox.Show(ID.ToString)
End Sub
End Class
You need to call InitializeComponent() so the controls will show up.
How do you usually get data into objects? You set a property or pass an argument to a method or constructor? Why should this be any different? Decide which you want to use and then write that code in your form. If it's required data, I would suggest a constructor. Just write this code in your form:
Public Sub New
and hit Enter. That will generate a little extra code automatically. You can then add a field to store the value, a parameter to the constructor and then assign the parameter to the field inside.
Thank you for pointing me to the correct route.
I solved it like this (which works fine and which is hopefully acceptable):
In the calling form:
Private Sub dgdata_dbclick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dg_data.CellMouseDoubleClick
Dim selectedRow As Integer = dg_data.CurrentCell.RowIndex
Dim f_edit As New edit
f_edit.edit(dg_data.Item(0, selectedRow).Value.ToString)
f_edit.ShowDialog(Me)
f_edit.Dispose()
End Sub
In the called form:
Public Sub edit(ByVal id As Long) 'Handles MyBase.Load
'Enter commands to prepare your form
End Sub

VB .NET event handler of controls

I'm using VB .NET to create a planning, and I got a little problem with events.
In the main form, I put a panel in which I add programatically rows and boxes in those rows. I have inside the form a TextBox and the panel that contains all the boxes. I want to change a the text of the TextBox when I click on a box, so I use the AddHandler statement but it doesn't work. I tried to debug it and I realised that it actually calls the sub and inside it, I can see the changes it makes (TextBox.Text becomes what I want), but when it exits the sub, it is like nothing has changed.
I don't know if I was clear enough.
Thanks
Here is a simplified code (I removed all the graphics functions to resize the controls...)
Public Class frmPrinc
Public actEditing As Object
Private Class boxAct
Inherits Label
Public act As Integer
Public Sub New(ByVal a As Integer)
act = a
AddHandler Me.Click, AddressOf clickBox
End Sub
Private Sub clickBox(sender As Object, e As EventArgs)
Dim boxact As boxAct = DirectCast(sender, boxAct)
frmPrinc.actEditing = boxact
boxact.Text = "Clicked"
End Sub
End Class
Private Sub showPlanning()
pan_plan.Controls.Clear()
Dim plan As New Control ' Control that will be used as a row
For i As Integer = 0 To 10
plan.Controls.Add(New boxAct(i))
Next
Panel1.Controls.Add(plan)
End Sub
End Class
When I run that, the text of the box changes but actEditing is still Nothing...
Instead of boxAct trying to directly update frmPrinc of the current "box" being clicked, it should instead raise a Custom Event that frmPrinc subscribes to. frmPrinc can use that information as it then sees fit. Below I've added the custom event and raise it in class boxAct. The form subscribes to that event using AddHandler when each instance of boxAct is created. All together, this looks something like:
Public Class frmPrinc
Public actEditing As boxAct
Public Class boxAct
Inherits Label
Public act As Integer
Public Event BoxClicked(ByVal box As boxAct)
Public Sub New(ByVal a As Integer)
act = a
End Sub
Private Sub boxAct_Click(sender As Object, e As EventArgs) Handles Me.Click
Me.Text = "Clicked"
RaiseEvent BoxClicked(Me)
End Sub
End Class
Private Sub showPlanning()
pan_plan.Controls.Clear()
Dim plan As New Control ' Control that will be used as a row
For i As Integer = 0 To 10
Dim box As New boxAct(i)
AddHandler box.BoxClicked, AddressOf box_BoxClicked
plan.Controls.Add(box)
Next
Panel1.Controls.Add(plan)
End Sub
Private Sub box_BoxClicked(box As boxAct)
actEditing = box
Debug.Print("Box Clicked: " & actEditing.act)
End Sub
End Class
From the comments:
Thanks man, it worked! I'd like to know though why I need to make such
a structure to raise a simple event that modifies the main form...
Just to not do the same mistake again – Algor Frile
This a design decision everyone must make: "Loosely Coupled" vs. "Tightly Coupled". The approach I gave above falls into the Loosely Coupled category. The main benefit to a loosely coupled solution is re-usability. In your specific case, we have Class boxAct being reused multiple times, albeit all within the same form. But what if that wasn't the case? What if you wanted to use boxAct on multiple forms (or even have multiple "groups" of them)? With your original approach, you had this line:
frmPrinc.actEditing = boxact
which means that if wanted to use Class boxAct with a different form you'd have to make a copy of Class boxAct, give it a new name, and then manually change that one line to reference the new form:
Public Class boxAct2
Private Sub clickBox(sender As Object, e As EventArgs)
Dim boxact As boxAct = DirectCast(sender, boxAct)
frmSomeOtherForm.actEditing = boxact
boxact.Text = "Clicked"
End Sub
End Class
This shows the disadvantage of the Tightly Coupled approach, which uses references to specifics types (the Form in this case) to communicate. The Tightly coupled approach might be initially easier to implement when you're coding fast and furious, but then it suffers from re-usability down the line. There are scenarios in which a tightly coupled solution make sense, but only you can make that decision; those scenarios usually involve some kind of "sub-control" that will only ever get used within some kind of custom container/control and will never be used on its own somewhere else.
Conversely, with the loosely coupled approach, if we wanted to re-use Class boxAct in a different form, then no changes to it would be required at all (though at that point you'd probably not want it declared within your original Form!). In the new form you'd simply add a handler for the BoxClicked() event and then do what you need to do. Each form would receive the events for its respective instances of boxAct.
Final thoughts...your original approach could actually work, but most likely was failing at this line (same as above):
frmPrinc.actEditing = boxact
Here you were referencing to frmPrinc using what is known as the Default Instance of that form. This would have worked if frmPrinc was the "Startup Object" for your application. I'm guessing it wasn't, however, and you were creating an instance of frmPrinc from somewhere else. To make the original approach work you would have had to pass a reference to your ACTUAL instance of frmPrinc into Class boxAct (usually via the Constructor in tightly coupled solutions).

passing form textbox value to another form in same project

I currently have 2 forms. 1 holds user data and is modifiable. The other is a display/read only form.
I am trying to pull data (tbUSI.text) from ReportSettings and pass it to Form1 and display it on my UserData.text control.
I have tried using public properties but to no avail. I would rather use public properties, since its cleaner. Here is the code im using to set the property:
Public Property UserSignedInto As String
Get
Return tbUSI.Text
End Get
Set(value As String)
End Set
End Property
Here is my code attempting to call that property on the main form (form1)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
UserData.text = UserSignedInto
End Sub
It doesn't pull anything, the textbox on my main form is blank.
Take a look here:
SCCMReports = New ReportSettings
UserData.text = SCCMReports.UserSignedInto
When was the SCCMReports form ever displayed to the user? Since it was never displayed, its tbUSI.Text value will of course be empty because the user never had an opportunity to enter text.
It sounds like you need a reference to an existing instance of ReportSettings, rather than creating a new instance. Where do you have that existing instance?
If Form1 created the instance in another block of code, store it in a class-level member on Form1 (perhaps called SCCMReportsInstance or something of that nature). When the instance is created, set it to the value of that property and reference that property in your code:
UserData.text = Me.SCCMReportsInstance.UserSignedInto
If the ReportSettings form is instead creating the instance of Form1 then it can pass a reference to itself. You'd still have a property on Form1, it would just be set in the constructor. Something like this:
Sub New(ByVal sccmReportsInstance As ReportSettings)
Me.SCCMReportsInstance = sccmReportsInstance
End Sub
So when initializing the Form1 instance, you'd pass the reference:
Dim form1 As Form1
form1 = New Form1(Me)
form1.Show()
Any way you go about it, you need to access the existing instance of the displayed form in order to access its properties. A new instance would have new versions of those properties and wouldn't have the same values.

How to call new class with global scope from function

Salvete! I want to create a new instance of a class when I click on a button, but I need to interact with members of that class using other controls on the form, so I need the class to have a global scope. I know I could call the new class in formload, but the class creates certain variables that need to be current. If it is done on formload, the variables wouldn't be current, because if the class is created at formload, it would have different info at that time. Surely, there must be a way to create an instance of the myClass from within a sub that is accessible from other subs of the same class.
Something like this:
click on myForm.button1 to dim myclassInstance as new myClass
then, click on myForm.button2, to make mylabel.text = myClassiInstance.myvariable
First, you may be having problems because MyClass is a reserved keyword, so if you are trying to create a class with this name, you will need to choose a different class name.
Having said that, I believe that what you want is something similar to the following, where we store the instance of the class as a private member of the form. This instance will only "live" as long as the form exists, so if it needs to be share among forms, then you will need to move the instance to a different location (a global variable or a shared member).
Public Class Form1
Private m_CurrentInstance As ThisIsMyClass
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
m_CurrentInstance = New ThisIsMyClass
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
If m_CurrentInstance IsNot Nothing Then
myLabel.Text = m_CurrentInstance.MyVariable
End If
End Sub
End Class
It's difficult to understand what you are asking for but it seems like you just want to check out the Shared keyword. Basically Shared means single instance.
So whatever you apply it to will be created only once and be tied to the type of the class not an instance of it.
Private Shared sharedInt As Integer
Private Shared sharedPropValue As String
How to create and use shared members by using Visual Basic .NET