Multiple forms, instances and using them in a class. VB.net - vb.net-2010

I have an MDI Parentform and it has multiple forms which are declared as :
Partial Public Class Giriş
Private clients As thirdperson
Private suppliers As thirdperson
Private activecontrol As thirdperson
Private proposals As transactions
Private addproposal As addtransaction
private sales as transactions
private addsales as addtransation
Private products As products
...
...
I also have a dataworks sub which works as a class but i code it in form and also for each form as below :
'for example for pressing add new button
If ActiveControl Is **clients** Or ActiveControl Is **suppliers** Then
activeform.dataworks (counter, "add new")
ElseIf ActiveControl Is products Then
products.dataworks (counter, "add new")
ElseIf ActiveControl Is addproposal Then
addproposal.dataworks (counter, "add new")
End If
i need a way to figure out how to use a vairble name for instance name. (I don't want to define each instance name everytime i just want to know if there is a way to use like below :
'for example
dim formvariable as form
formvariable.dataworks(counter,"add new") --> just want to use this and assign the value for products, proposals vs to this variable.
is this possible?
Thanks for your help in advance.
Sertac.

The proper way would be to develop an Interface that all of your Child Forms Implement. That interface would have a dataworks() method. Then you can cast the current mdichild to the interface type and run the method. This would result in strongly typed coded that makes sense.
If you just want to hack your way through it, though, then attempt grab the dataworks() sub from the current mdichild using reflection and execute it like below:
Dim frm As Form = Me.ActiveMdiChild
If Not IsNothing(frm) Then
Dim MI As Reflection.MethodInfo = frm.GetType.GetMethod("dataworks", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.IgnoreCase)
If Not IsNothing(MI) Then
MI.Invoke(frm, New Object() {counter, "add new"})
End If
End If
Here's a quick example of the Interface approach:
Public Interface Data
Sub DataWorks(ByVal counter As Integer, ByVal msg As String)
End Interface
This is what thirdperson looks like implementing the interface:
Public Class thirdperson
Implements Data
Public Sub DataWorks(counter As Integer, msg As String) Implements Data.DataWorks
Debug.Print(counter & ", " & msg)
End Sub
End Class
All of your mdichild forms would have to be modified in a similar approach.
Then the code in the MdiParent would change to:
Dim frm As Form = Me.ActiveMdiChild
If TypeOf frm Is Data Then
Dim D As Data = DirectCast(frm, Data)
D.DataWorks(counter, "add new")
End If

Related

Listbox breaks when setting it to a virtual instance from a class

I have a weird problem that I can't wrap my head around.
I have the following code:
Public Class Form1
Public WithEvents MyClass1 As New MyClass
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
Private Sub MyClass_UpdateListbox() Handles MyClass1.UpdateListbox
For Each sItem as String In MyClass1.Listbox
MsgBox(sItem) 'an MsgBox shows correct items each time.
Next sItem
Me.Listbox = Me.MyClass1.Listbox 'doesn't work and breaks listbox.
Me.Listbox.Items.Clear() 'listbox is empty anyway, but has no effect.
Me.Listbox.Items.Add("event triggered") 'does nothing.
End Sub
End Class
Public Class MyClass
Public Listbox as new Listbox
Public Event UpdateListbox()
Public Sub New()
'Constructor. sub.
Me.AddItem("Populating listbox")
End Sub
Public Sub AddItem(sItem as String)
Me.Listbox.Items.Add(sItem)
RaiseEvent UpdateListbox()
End Sub
End Class
If I comment the following lines in above code, the listbox keeps adding event triggered, as expected. Of course, I don't have to remove the clear one. It will work, but then it just adds the same item. If I use a command button and call MyClass.AddItem("Something") that is correctly added too as long as the below is commented out. But if not, then once the listbox is in broken state, nothing can be added anymore.
Me.Listbox = Me.MyClass1.Listbox 'doesn't work and breaks listbox.
Me.Listbox.Items.Clear() 'listbox is empty anyway, but has no effect.
How can I use a virtual listbox and assign it to my real listbox?
Also, instead of assigning one listbox to the other, I can of course use that for each loop and add each item one by one which works, but that for each look was for debugging purpose in the first place.
EDIT:
My goal with this application is to build a Todo list with features that are not in a todolist. This is a project I build for work because there I need a tool like this. I already have a todolist that I use but I built it wrong in the past. Everything was condensed in form1, no modules no extra classes. As a result I got weird bugs that I patched with workarounds. I am now rebuilding the application from the ground up, separating tasks in its own classes so I can apply business logic and have a true OOP application. The todo list will become its own class, and managing the list etc will be handeled by this class. It interacts with controls on the form, such as buttons and listboxes. If I just use form1.listbox from the class, things break at program start. I started another question and the below code was a now deleted answer. At first I did not get it working because I did not realize the listbox crashes if I assign it the virtual instance.
So my goal is to have the todolist be handled entirely by the todolist class. It does need a way to interact with controls on form1, and that is the puzzle I'm currently trying to solve.
In the original code, the main problem is that the Field that hold the instance of a Control shown if a Form is reassigned to the instance of another ListBox Control defined in a custom class:
Me.Listbox = Me.MyClass1.Listbox
From now on, Me.Listbox points another ListBox that is not show on screen, so any attempt to update the Form's child ListBox fails, except when Me.Listbox.Items.Clear() is called - in the same procedure - after it's being reassigned, because the handle of the Owner of the ObjectCollection (the object that holds the Items shown in the ListBox) has not been updated yet. It's going to fail after the current method exits nonetheless.
As noted in comments, this is a simplified method to handle a Form and its child Controls using a handler class. The contract between the class handler and a Form is sealed by an Interface (named IFormHandler here).
A Form that implements this Interface exposes the methods defined by the Interface that allow to trigger Actions and specific behaviors, depending on the Type of Control and the implementation.
I suggest to take a look at the MVP or ReactiveUI (MVVM-derived) for WinForms Patterns.
How too proceed:
Open up the ApplicationEvents class object.
If you don't have it already, select Project -> Properties -> Application and click the View Application Events button. It will generate ApplicationEvents.vb. Find it in Solution Explorer and open it up.
It should look like this (plus a bunch of comments that explain what it's for):
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
Partial Friend Class MyApplication
End Class
End Namespace
Paste into MyApplication these lines of code:
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
Partial Friend Class MyApplication
Public SomeFormHandler As MyFormHandler(Of SomeForm)
Protected Overrides Function OnStartup(e As StartupEventArgs) As Boolean
SomeFormHandler = New MyFormHandler(Of SomeForm)
Return MyBase.OnStartup(e)
End Function
End Class
End Namespace
Add an Interface that defines the Actions (or Behaviors) that a Form must implement.
Here, the GetUsersList() method specifies that a Form that implements this Interface must return the instance of a child ListBox Control.
(To add an Interface, select Project -> Add -> New Item... and select the Interface template. Name the file IFormHandler)
Extend this Interface as needed, to add more Methods or Properties that define actions and behaviors.
Public Interface IFormHandler
Function GetUsersList() As ListBox
End Interface
A Form that implements the IFormHandler Interface implements and exposes the GetUsersList() method, which returns the instance of a ListBox Control (named usersList here)
There's nothing else to do with this Form, the control is handed over to the MyFormHandler object that is initialized with this Type.
Public Class SomeForm
Implements IFormHandler
Public Sub New()
InitializeComponent()
End Sub
Public Function GetUsersList() As ListBox Implements IFormHandler.GetUsersList
Return Me.usersList
End Function
End Class
Now, to show SomeForm, you can use the MyFormHandler class object show below.
' Set the Owner if called from another Form
My.Application.SomeFormHandler.Show(Me)
' Or without an Owner
My.Application.SomeFormHandler.Show()
To close SomeForm, you can either use its handler:
My.Application.SomeFormHandler.Close()
or close it as usual:
[SomeForm Instance].Close()
If MyFormHandler determines that the instance of SomeForm has been disposed, it creates a new one when you call its Show() method again later.
To update the ListBox Control of SomeForm, use the public methods exposed by the MyFormHandler class:
' Add a new element
My.Application.SomeFormHandler.UpdateUsersList(UpdateType.AddElement, "Some Item")
' Remove an element
My.Application.SomeFormHandler.UpdateUsersList(UpdateType.RemoveElement, "Some Item")
' Replace an element
My.Application.SomeFormHandler.UpdateUsersList(UpdateType.ReplaceElement, "New Item", "Some Item")
' Clears the ListBox
My.Application.SomeFormHandler.ClearUsersList()
All these actions generate an event that you can subscribe to when needed.
See also the example that shows how to raise a custom event when the ListBox raises one of its stardard events; SelectedIndexChanged is handled here.
See the implementation of MyFormHandler.
Generic Form handler:
A Form needs to implement the IFormHandler Interface for the MyFormHandler class to accept it as valid.
You can of course extend the Interface, to add more Actions, or build a MyFormHandler class object that uses a different Interface, or more than one.
Public Class MyFormHandler(Of TForm As {Form, IFormHandler, New})
Implements IDisposable
Private formObject As TForm
Private IsInstanceSelfClosing As Boolean = False
Public Event UsersListUpdate(item As Object, changeType As UpdateType)
Public Event UsersListIndexChanged(index As Integer)
Public Sub New()
InitializeInstance()
Dim lstBox = formObject.GetUsersList()
AddHandler lstBox.SelectedIndexChanged, AddressOf OnUsersListIndexChanged
End Sub
Private Sub InitializeInstance()
formObject = New TForm()
AddHandler formObject.FormClosing, AddressOf OnFormClosing
End Sub
Private Sub OnFormClosing(sender As Object, e As FormClosingEventArgs)
IsInstanceSelfClosing = True
Dispose()
End Sub
Public Sub UpdateUsersList(updateMode As UpdateType, newItem As Object, Optional oldItem As Object = Nothing)
If newItem Is Nothing Then Throw New ArgumentException("New Item is null")
Dim lstBox = formObject.GetUsersList()
Select Case updateMode
Case UpdateType.AddElement
lstBox.Items.Add(newItem)
Case UpdateType.RemoveElement
lstBox.Items.Remove(newItem)
Case UpdateType.ReplaceElement
If oldItem Is Nothing Then Throw New ArgumentException("Replacement Item is null")
Dim index = lstBox.Items.IndexOf(oldItem)
lstBox.Items.Remove(oldItem)
lstBox.Items.Insert(index, newItem)
Case Else : Return
End Select
RaiseEvent UsersListUpdate(newItem, updateMode)
End Sub
Public Sub ClearUsersList()
formObject.GetUsersList().Items.Clear()
End Sub
Private Sub OnUsersListIndexChanged(sender As Object, e As EventArgs)
RaiseEvent UsersListIndexChanged(DirectCast(sender, ListBox).SelectedIndex)
End Sub
Public Sub Show(Optional owner As IWin32Window = Nothing)
If formObject Is Nothing OrElse formObject.IsDisposed Then InitializeInstance()
If formObject.Visible Then
formObject.WindowState = FormWindowState.Normal
formObject.BringToFront()
Else
formObject.Show(owner)
End If
End Sub
Public Sub Close()
If formObject IsNot Nothing AndAlso (Not formObject.IsDisposed) Then
RemoveHandler formObject.FormClosing, AddressOf OnFormClosing
IsInstanceSelfClosing = False
Dispose()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overridable Sub Dispose(disposing As Boolean)
If disposing Then
If formObject Is Nothing OrElse formObject.IsDisposed Then Return
Dim lstBox = formObject.GetUsersList()
RemoveHandler lstBox.SelectedIndexChanged, AddressOf OnUsersListIndexChanged
RemoveHandler formObject.FormClosing, AddressOf OnFormClosing
If Not IsInstanceSelfClosing Then formObject.Close()
IsInstanceSelfClosing = False
End If
End Sub
End Class
Enumerator used in MyFormHandler:
Public Enum UpdateType
AddElement
RemoveElement
ReplaceElement
End Enum

String form name to form reference

Basically, I am rewriting some code working for years. Over the time I have many (60+) references to forms - there's a menuitem with OnClick event for each form, where a form reference was created:
Private Sub SomeForm_Click(sender As Object, e As EventArgs) Handles MenuItemForSomeForm.Click
NewTab("Some Form", New SomeForm, 0)
End Sub
...where first parameter is a name to put in a tabPage.Text where the form is opened, second is a new instance of the (particular) form SomeForm and 0 is a default record to display (0 means no default record).
Now, I created a dynamic menu and stored the form names in a database (due to better access control over the access rights, etc). Now, because the menu is generated at runtime, I can't have the OnClick event with separate instance definition of the form and have to create it at runtime, after the MenuItems are created. The side-effect idea was to cut the code short by using only 1 OnClick event or such with MenuItem.Tag paremeter as FormName. Something like:
Private Sub clickeventhandler(sender As Object, e As EventArgs)
Dim tsmi As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
Dim newForm As New >>>FormFrom(tsmi.Tag.ToString)<<< ' only explanation, this won't work
MainW.OpenModuleInTab(new newForm, tsmi.Tag.ToString, 0)
However I am failing to find a way to create form (instances) from this string reference. Reference through collection (i.e. List(of) or Dictionary) would be fine too, I believe.
The structure is obviously:
Object → Form → Form1 (class) → MyForm1 (instance)
I know I can create an object like this:
' Note that you are getting a NEW instance of MyClassA
Dim MyInstance As Object = Activator.CreateInstance(Type.GetType(NameOfMyClass))
I can re-type it to a Form type:
Dim NewForm as Form = CType(MyInstance,Form)
... to acccess some of the form properties like Width, TopLevel, etc., but that's about it. I can't do:
Dim NewForm1 as Form1 = CType(NewForm,Form1)
...because obviously, Form1 comes as a string "Form1".
I don't know how to create a Form1 reference from a "Form1" text (then it would be easy to create an instance) or how to create an instance directly (MyForm1).
SOLUTION
As sugested, I used reflection to get the form. The only way working for me I found was this:
Dim T As Type = System.Type.GetType(FormName, False)
If T Is Nothing Then 'if not found prepend default namespace
Dim Fullname As String = Application.ProductName & "." & FormName
T = System.Type.GetType(Fullname, True, True)
End If
Dim f2 As New Form ' here I am creating a form and working with it
f2 = CType(Activator.CreateInstance(T), Form)
f2.TopLevel = False
f2.Name = FormName.Replace(" ", "") & Now.ToString("yyyyMMddmmhh")
f2.FormBorderStyle = FormBorderStyle.None
f2.Dock = DockStyle.Fill
I am using VB.net CallByName to set public variable and same function to run a sub method (every form contains RecordID variable and LoadRecords sub):
CallByName(f2, "RecordID", CallType.Set, 111)
CallByName(f2, "LoadRecords", CallType.Method, Nothing)
For testing purposes, I put following into the testing form:
Public RecordID As Int32
Public Sub LoadRecords()
MsgBox("Load records!!!!" & vbCrLf & "RecordID = " & RecordID)
End Sub
Activator.CreateInstance(TypeFromName("Form1"))
TypeFromName Function:
Dim list As Lazy(Of Type()) = New Lazy(Of Type())(Function() Assembly.GetExecutingAssembly().GetTypes())
Function TypeFromName(name As String) As Type
Return list.Value.Where(Function(t) t.Name = name).FirstOrDefault()
End Function
So, let's go with the idea that I have an assembly called "WindowsApp2" and in that assembly I've defined Form1 and Form2. I've also created this module in the same assembly:
Public Module Module1
Public Function GetDoStuffWiths() As Dictionary(Of Type, System.Delegate)
Dim DoStuffWiths As New Dictionary(Of Type, System.Delegate)()
DoStuffWiths.Add(GetType(WindowsApp2.Form1), CType(Sub(f) WindowsApp2.Module1.DoStuffWithForm1(f), Action(Of WindowsApp2.Form1)))
DoStuffWiths.Add(GetType(WindowsApp2.Form2), CType(Sub(f) WindowsApp2.Module1.DoStuffWithForm2(f), Action(Of WindowsApp2.Form2)))
Return DoStuffWiths
End Function
Public Sub DoStuffWithForm1(form1 As Form1)
form1.Text = "This is Form 1"
End Sub
Public Sub DoStuffWithForm2(form2 As Form2)
form2.Text = "This is Form 2"
End Sub
End Module
Now, in another assembly "ConsoleApp1" I write this:
Sub Main()
Dim DoStuffWiths As Dictionary(Of Type, System.Delegate) = WindowsApp2.Module1.GetDoStuffWiths()
Dim formAssembly = System.Reflection.Assembly.Load("WindowsApp2")
Dim typeOfForm = formAssembly.GetType("WindowsApp2.Form1")
Dim form As Form = CType(Activator.CreateInstance(typeOfForm), Form)
DoStuffWiths(typeOfForm).DynamicInvoke(form)
Application.Run(form)
End Sub
When I run my console app I get a form popping up with the message "This is Form 1".
If I change the line formAssembly.GetType("WindowsApp2.Form1") to formAssembly.GetType("WindowsApp2.Form2") then I get the message "Wow this is cool".
That's how you can work with strongly typed objects that you dynamically instantiate.
Dim AssemblyProduct As String = System.Reflection.Assembly.GetExecutingAssembly().GetName.Name
Dim FormName As String = "Form1"
Dim NewForm As Object = Reflection.Assembly.GetExecutingAssembly.CreateInstance(AssemblyProduct & "." & FormName)
If TypeOf (NewForm) Is Form1 Then
Dim NewForm1 As Form1 = CType(NewForm, Form1)
NewForm1.BackColor = Color.AliceBlue
NewForm1.Show()
End If

showing a new Form Using a public function in vb.net

In my application I have to show lot of forms when particular button/panels are clicked. so instead of writing
Frm = New formname
Frm.MdiParent = MDIParent
Frm.Show()
i want to have public function through which i can pass the form name.
for that i have written a function
Public Sub showForm(ByVal formname As Form)
Frm = New formname
Frm.MdiParent = MDIParent1
Frm.Show()
End Sub
Call showForm(myformname)
but problem with this is, it says formname is not defined
EDIT:
I updated my answer to reflect your comment that a form should only be opened once.
I want to have public function through which i can pass the form name.
for that i have written a function
Public Sub showForm(ByVal formname As Form)
You don´t pass the name of a form to your function but an object of type Form instead.
Here is one possible solution with a generic version of showForm:
Public Class FormManager
Private _formByName As New Dictionary(Of String, Form)
Public Sub showForm(Of T As {Form, New})(name As String, parent As Form)
Dim frm As Form = Nothing
If Not _formByName.TryGetValue(name, frm) OrElse _formByName(name).IsDisposed Then
frm = New T()
_formByName(name) = frm
End If
frm.MdiParent = parent
frm.Show()
End Sub
End Class
The FormManager holds a dictionary cache for all opened forms with Key=form name. This is to make sure that a form is only opened once. The check form.IsDisposed makes sure that you can close the form and reopen it.
Usage from the parent form:
Public Class Form1
Private fm = New FormManager()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
fm.showForm(Of MyForm)("MyForm", Me)
End Sub
End Class
The first parameter is to identify the form name. The real magic is in the Type T which we made sure it is 1) of type or subtype Form and 2) it has a parameterless constructor (MyForm is just a placeholder for this example put in the type of your real form you want to show).
The parent parameter will bring you additional flexibility if it is not always MDIParent1. Remove it if you don´t neet the extra flexibility.
For sure you can also drop the FormManager class and put the showForm to another place.

Dynamically Open form

I have third party control called TopicBar which looks like window xp navigation bar that you can see on the left side of a window
In my application I've 71 forms I have created a DB table to store all my forms Name and Text,A partcular user can add which forms(by searching form's name that I have already stored in a table) should add in topicbar as item.
For example, A form called FrmBill and its name is Bill once a user addes Bill to Topicbar it will successfully creates an item with name Bill,Now what I'm facing is I need to open the form FrmBill when that user clicks on Bill item in topicbar
I can get form name ie FrmBill is a user clicks in Bill but I couldnt open the form becuase I need to dynamically create instance or call Form here frmBill
what I have done so far
Dim formName As String
Dim frm As Form
Using conn As New SqlConnection(conn_str)
conn.Open()
Dim cmd As SqlCommand
cmd = New SqlCommand("", conn)
cmd.CommandText = "select top 1 formname from winforms where id=" & winformid & ""
formName = cmd.ExecuteScalar
'output: FrmBill
frm = New Form
frm.Name = formName '(FrmBill)
With frm
.MdiParent = FrmMain
.Show()
.Focus()
End With
End Using
Note: in this function I can get form name ie FrmBill from the DB, but its not showing the actual form FrmBill
So is there anyways to declare form name like below
dim frm as new "& formname &"
It's not so easy as "dim frm as new "& formname &"", but it can be done doing something like this:
Dim frmNewForm As Form = Nothing
Dim frmNewForm_Type As Type = Type.GetType("your_assembly.type_name")
frmNewForm = CType(Activator.CreateInstance(frmNewForm_Type), Form)
I mean, you have to store the Type of the class form.
Edit:
You can open a form so:
Dim oFr as New frmBill
oFr.Show()
ok? Well. You can open a Form so:
Dim oFr as Form
oFr = New frmBill ' Because frmBill inherits System.Windows.Forms.Form class. All forms do it
oFr.Show()
Here, frmBill its the Type of the class. You have no frmBill explicit reference, so you have to load it dinamically.
How can be do? Using a "NET mechanism" called Reflection. You can create a class dinamically, specifing the class Type.
Dim frmNewForm As Form = Nothing
Dim frmNewForm_Type As Type = Type.GetType("project_name.frmBill")
frmNewForm = CType(Activator.CreateInstance(frmNewForm_Type), Form)
What is Plutonix saying? Suppose you have a public "Print" method in frmBill.
With my answer, you can't do this:
frmNewForm.print()
Because you only can access to System.Windows.Forms.Form methods (close, show, showdialog...)
You can improve this, using a custom base class, or using Interfaces, or base abstract class... as you need. You can combine different ideas, depending on what you need. For example, you can combine an Interface with a Superclass:
public class frmMyForms
inherits System.Windows.Forms.Form
public sub store_data ()
....
public interface IInterface_common_methods
sub print ()
...
public class frmBill
inherits frmMyForms
implements IInterface_common_methods
public overloads sub store_data ()
msgbox ("store")
end sub
public sub print () implements IInterface_common_methods.print
msgbox ("print")
end sub
Now, you could do things like:
Dim frmNewForm As frmMyForms= Nothing
Dim frmNewForm_Type As Type = Type.GetType("project_name.frmBill")
frmNewForm = CType(Activator.CreateInstance(frmNewForm_Type), frmMyForms)
frmNewForm.Show()
frmNewForm.store_data()
ctype(frmNewForm, IInterface_common_methods).Print()
I don't know if this is wthat you're looking for, but I hope this can help you to learn more about NET possibilities.
Dim str As String = "NAME OF FORM"
Dim formName As String = Me.GetType().Namespace & "." & str.ToString()
Dim form = CType(Activator.CreateInstance(Type.GetType(formName)), Form)
form.StartPosition = FormStartPosition.CenterScreen
form.Show()
This is what I do to dynamically load a form based on user interaction.
I hope this helps someone.

VB.net MVC understanding and binding

I am trying to seperate my code logic from my gui as in MVC principles, what I am trying to achieve is quite simple I believe
I have my Form1, which contains a textbox and button, once the button is clicked it loads a function in my controller class which adds a string to a database using entity and then should update the textbox with this name.
I thought what I would need to do is pass the original form through and then databind to the textbox object on the form, this is where I have come unstuck though, as my logic fails...
Public Class Form1
Private mf As New MainForm(Me)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
mf.buttonClick()
End Sub
End Class
Public Class MainForm
Private Property a As Form
Public Sub New(ByVal s As Form)
a = s
End Sub
Function buttonClick() As Boolean
Dim context As TestDBEntities2 = New TestDBEntities2
Dim newCategory As tTable = New tTable
newCategory.Name = "Test1 " & Today.DayOfWeek
context.tTables.Add(newCategory)
context.SaveChanges()
Dim current As String = newCategory.Name
a.DataBindings.Add("text", "TextBox1", current)
Return True
End Function
End Class
and my error:
Cannot bind to the property or column Test1 6 on the DataSource.
Am I looking at this the right way? Or am I so far off that there is an obvious reason this doesn't work?
Any input would be appreciated! Whats the best way to pass data back to a source without returning it in as a result of a function?
You should consider changing your code a bit, so that it reflects more the MVC structure:
use Events to exchange data and indicate action triggers, instead of using the form object
normally the controller has knowledge of the form and not the other way around, so swap this in your project. This reflects also the first point
So a possible solution for a Windows Forms application could look like this:
The form that has one button and one text field, one event to signal the button click and one WriteOnly property to fill the TextBox from outside the form:
Public Class MainForm
Public Event GenerateAndShowEvent()
' allow text box filling
Public WriteOnly Property SetTextBoxContent()
Set(ByVal value)
generatedInputTextBox.Text = value
End Set
End Property
Private Sub generateAndShowButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles generateAndShowButton.Click
' forward message: inform the subscriber that something happened
RaiseEvent GenerateAndShowEvent()
End Sub
End Class
The controller class has knowledge of the form, creates it, binds (listens) to the form's button click event and makes the form ReadOnly for the world (exposes the form):
Public Class MainFormController
' the form that the controller manages
Private dialog As MainForm = Nothing
Public Sub New()
dialog = New MainForm()
' bind to the form button click event in order to generate the text and response
AddHandler dialog.GenerateAndShowEvent, AddressOf Me.GenerateAndShowText
End Sub
' allow the world to access readonly the form - used to start the application
Public ReadOnly Property GetMainForm()
Get
Return dialog
End Get
End Property
Private Sub GenerateAndShowText()
' create the text
Dim text As String = "Test test test"
' access the Database ...
' give the generated text to the UI = MainForm dialog!
dialog.SetTextBoxContent = text
End Sub
End Class
Now what left is to create first the controller, that creates the form and use the form to show it. This can be done like this:
Create an AppStarter module with a Main method:
Module AppStarter
Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
' create the controller object
Dim controller As MainFormController = New MainFormController()
' use the public property to get the dialog
Application.Run(controller.GetMainForm)
End Sub
End Module
In your project settings uncheck the enable application framework setting and set the AppStarter module as the start point of your project:
Now you have a Windows Form Project using the MVC pattern.
If you still want to use DataBinding for the TextBox control, then create a Data Transfer Object or DTO that represents the fields you will transfer from your controller to the form:
Public Class DataContainer
Private t As String
Private i As Integer
Public Property Text() As String
Get
Return t
End Get
Set(ByVal value As String)
t = value
End Set
End Property
Public Property Id() As Integer
Get
Return i
End Get
Set(ByVal value As Integer)
i = value
End Set
End Property
End Class
Then add a BindingSource for your TextBox and configure it to use the DTObject:
Now bind the TextBox control to the DataBinding control:
What left is to add a public setter for the TextBox data binding control in the form:
Public Property TextBoxDataSource()
Get
Return TextBoxBindingSource.DataSource
End Get
Set(ByVal value)
TextBoxBindingSource.DataSource = value
End Set
End Property
and transfer the data from the controller:
Private Sub GenerateAndShowText()
' create the text
Dim text As String = "Test test test"
' access the Database ...
' give the generated text to the UI = MainForm dialog!
'dialog.SetTextBoxContent = text
Dim data As DataContainer = New DataContainer
data.Text = text
data.Id = 1 ' not used currently
dialog.TextBoxDataSource = data
End Sub
The binding can also be set programmatically - instead of doing this over the control property window, add the following code in the constructor of the form:
Public Sub New()
InitializeComponent()
' bind the TextBox control manually to the binding source
' first Text is the TextBox.Text property
' last Text is the DataContainer.Text property
generatedInputTextBox.DataBindings.Add(New Binding("Text", TextBoxBindingSource, "Text"))
End Sub
You seem to misunderstand the Add method.
The first argument is the name of the control's property to which you are binding. This should be "Text", not "text".
The second argument is an object that contains the data you want to bind. You have passed the name of the target control, rather than the source of the data. You are also binding to the form rather than the text box. So what you have said is that you want to bind the form's text property to data that can be extracted from the string "TextBox1".
The third argument says where to go to find the data. For example, if you passed a FileInfo object for the second argument, and you wanted to bind the file's path, you would pass the string "FullName", because that is the name of the property containing the data you want. So you have told the binding to look for a property on the string class called "Test1 6", which is why you have received the error message saying it can't be found.
I think what you want is
a.TextBox1.DataBindings.Add("Text", newCategory, "Name");