I am creating a browser in VB.Net using Cefsharp. I created a custom LifeSpanHandler to handle Popup windows, but when I try to call a Public Sub in a different class, it is not giving the expected output.
I created my LifeSpanHandler using the following code:
Public Class LifeSpanHandler
Implements ILifeSpanHandler
Public Event PopupRequest As Action(Of String)
Public Function OnBeforePopup(browser As IWebBrowser, sourceUrl As String, targetUrl As String, ByRef x As Integer, ByRef y As Integer, ByRef width As Integer, ByRef height As Integer) As Boolean Implements ILifeSpanHandler.OnBeforePopup
RaiseEvent PopupRequest(targetUrl)
My.Settings.newpage = targetUrl
Call Form1.IntNewTab()
Return True
End Function
Public Sub OnBeforeClose(browser As IWebBrowser) Implements ILifeSpanHandler.OnBeforeClose
End Sub
End Class
And then I have browser.LifeSpanHandler = New LifeSpanHandler where it is Initialized. (With browser being CefSharp.WinForms.ChromiumWebBrowser)
I save the targetURL in My.Settings.newpage, then when the browser is initialized, it opens to that URL. In a different class (and a different Form), I have this code:
Public Sub IntNewTab()
Dim tab As New TabPage
Dim newtab As New tabs
newtab.Show()
newtab.Dock = DockStyle.Fill
newtab.TopLevel = False
tab.Controls.Add(newtab)
Me.CustomTabControl1.TabPages.Add(tab)
Me.PictureBox1.Location = New System.Drawing.Point(PictureBox1.Location.X + 150, 3)
My.Settings.newpage = My.Settings.homepage
Me.CustomTabControl1.SelectedTab = tab
End Sub
Which is the code to add a new tab. But in my LifeSpanHandler, when I Call Form1.IntNewTab(), the browser freezes out of focus. The window is grayed out (meaning it's out of focus) and I can't drag it around, and it stays on top of everything else, and I can't interact with any part of the browser.
To test something else out, I added a button to Form1 with the exact code from IntNewTab, and when I click on it, it opens a new tab to the specified page like normal. I also tried leaving the button visible, and OnBeforePopup adding Form1.Button1.PerformClick, but that did not work either. Anyone else have experience with doing this, or have any suggestions?
Edit:
I added the following codes to my browser to try and get rid of the default instance (the best I understood it):
To Form1:
Module Program
Friend frmMain As Form1
End Module
In Form1_Load:
`frmMain = Me`
Then I added this code in the LifeSpanHandler to reflect the changes:
Dim mainFrm = New Form1()
mainFrm.IntNewTab()
And that did not work. It just kept freezing out of focus like before. I also tried adding frmMain.IntNewTab() (the code that is in Form1_Load), and it still did not work.
Related
I feel comfortable when instantiating a class, then using it within the same function, but now I need to instantiate a class from a toolbar button:
Dim pll As New Polyline()
Debug.WriteLine("TSB_Polyline_Click:: a new polyLine is born : " & pll.Count)
pll.AddPoint(0, 0)
And then I need to run the pll.AddPoint from my class method in another sub:
Public Sub MyEvent(sender as object, e as myEvent) Handles myEvent
Dim x as Double, y as Double
pll.AddPoint(x,y)
There I have my error (System.NullReferenceException: 'Object reference not set to an instance of an object.'), pll = Nothing (no error in my constructor, my class worked from the toolbar button_Click)
I tried to declare pll public and shared:
Public Shared pll As Polyline
And even to import it:
Imports oGIS.Polyline
Nothing works. My Class is instanciated in the first Sub (toolbar button), but somehow dies when leaving the Sub...
Is there any other solution that doing it using VB6 instead of VB.Net ?
I am a bit desesperate, I found no such topic anywhere on Google...
If you need to access a variable in more than one method then it should be declared outside all of them. It should be Private if you don't need to access it outside the type it's declared in and it should only be Shared if you specifically need all instances of the class accessing the same object. In your case, Private and not Shared is the obvious choice:
Private pll As Polyline
You now set that field in the first method and get it in the second.
I am guessing a partial look at your class would be something like this...
Public Class Polyline
Public ReadOnly Property CountOfPoints As Integer
Get
Return ListOfPoints.Count
End Get
End Property
Public Property ListOfPoints As New List(Of Point)
Public Sub AddPoint(x As Integer, y As Integer)
Dim p As New Point(x, y)
ListOfPoints.Add(p)
End Sub
End Class
Declare a class level (in this case the class is a Form) variable which can be seen and used by any method in your form.
Public Class Form1
Private LocalPolyLine As Polyline
'This is a local list, not part of the class
'It is a list of the number of instances of the class you have created
Private ListOfLines As New List(Of Polyline)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Pretend this is a button on your toolbar used to create a new line
'Each time you click this button you have a completely new line with no points
LocalPolyLine = New Polyline
'If you want to keep track of how many lines you have...
ListOfLines.Add(LocalPolyLine)
End Sub
Private Sub AddPoint(x As Integer, y As Integer)
Debug.Print($"Number of Lines is {ListOfLines.Count}")
Debug.Print($"Number of points on current line {LocalPolyLine.CountOfPoints}")
LocalPolyLine.AddPoint(x, y)
Debug.Print($"Number of points on current line {LocalPolyLine.CountOfPoints}")
End Sub
'To see all the points on the line...
Private Sub ViewPoints()
For Each p In LocalPolyLine.ListOfPoints
Debug.Print($"The coordinates of the point are {p.X},{p.Y}")
Next
End Sub
End Class
I create a number of sub-forms, each of the same class, from my parent form.
Each of the subforms defines an event that should cause the parent form to create a new subform of a different class. - However only the latest created sub form is having it's events handled.
Thinking that it is because I have reused the reference to the sub-form, I store each of the subforms in a linked list of type subform.
SubForm Code.
Public Class UserEditor
Property ID As Integer
Public Event EditGroup(ByRef group As GroupPrincipal, ByVal newWindow As Boolean, ByVal Source As Integer)
Public Sub New(ByVal U As UserPrincipal, ByVal theId As Integer)
InitializeComponent()
ID = theId
UserEditor1.DisplayUserPrincipal(U)
End Sub
Private Sub UserEditor1_GroupEdit(ByRef groupItem As GroupListItem) Handles UserEditor1.GroupEdit
RaiseEvent EditGroup(groupItem.Grp, False, ID)
End Sub
Code on parentform
Public Class ASADManager
Dim WithEvents UserEditorInstance As UserEditor
Dim WithEvents UserEditorList As New List(Of UserEditor)
This code triggered on selection of leafobject in a displayed list.
If TypeOf leafObject Is UserPrincipal Then
UserEditorInstance = New UserEditor(currentLeaf.Principal, UserEditorList.Count) With {
.Text = currentLeaf.SamAccountName
}
UserEditorInstance.Show()
UserEditorInstance.Activate()
UserEditorList.Add(UserEditorInstance)
End If
Private Sub EditGroup(ByRef grp As GroupPrincipal, newWindow As Boolean, Source As Integer) Handles UserEditorInstance.EditGroup
If Not newWindow Then
Dim GEdit As New GroupEditorForm(grp) With {
.Text = grp.Name
}
GEdit.Show()
End If
End Sub
Everything works fine in the latest opened UserEditorInstance, it captures and re-throws the event, and that is cause by the parent, which opens the GroupEditor form.
However, if I select a 2nd LeafObject (to open another UserEditor window) it's events are trapped, and not the previous.
How do I trap both?
Hope this explanation makes it clear what I am doing (and doing wrong)
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.
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");
I have a form with a tabcontrol and 4 tabs. I want to open a form with showdialog in a predetermined tab.
I've tried
OptionsForm.OPTS_TabControl1.SelectTab(1)
OptionsForm.OPTS_TabControl1.ShowDialog()
but it didn't work.
Any help? thanks
First Kudos for using Stackoverflow. It shows you paid attention to class =D
regarding your question, that piece of code you showed should be working. You should provide the actual error so we can try to figure out.
Does OptionsForm refer to the class or an object of a class you created?
Anyways, try to create an object of the form and then set the starting tab, like this:
Dim OptionsObject As New OptionsForm
OptionsObject.OPTS_TabControl1.SelectTab(1)
OptionsObject.OPTS_TabControl1.ShowDialog()
Another solution might be Overloading the Showdialog method, although it seems kind of an overshot.
Here's how:
Inside your OptionsForm Code:
Public Overloads Sub Showdialog(ByRef TabNumber As Integer)
OPTS_TabControl1.SelectTab(TabNumber)
Return MyBase.ShowDialog()
then call the form using
optionsform.showdialog(1)
Note: Overloading is basically creating another instance of a subrotine that accepts different arguments. read the pages 342-358 of the manual if you wish to know more.
Since you are getting a NullReferenceException, you should separate out the call to see where the null object is. Is the tab control null?.
Imports System.Drawing
Imports System.Windows.Forms
Public Class Form1
Inherits Form
Private tabControl1 As TabControl
Private tabPage1 As TabPage
Private tabPage2 As TabPage
Private Sub MyTabs()
Me.tabControl1 = New TabControl()
Me.tabPage1 = New TabPage()
Me.tabPage2 = New TabPage()
Me.tabControl1.Controls.AddRange(New Control() {Me.tabPage1, Me.tabPage2})
Me.tabControl1.Padding = New Point(15, 10)
Me.tabControl1.Location = New Point(35, 25)
Me.tabControl1.Size = New Size(220, 220)
' Selects tabPage2 using SelectedTab.
Me.tabControl1.SelectedTab = tabPage2
Me.tabPage1.Text = "tabPage1"
Me.tabPage2.Text = "tabPage2"
Me.Size = New Size(300, 300)
Me.Controls.AddRange(New Control() {Me.tabControl1})
End Sub
Public Sub New()
MyTabs()
End Sub
Shared Sub Main()
Application.Run(New Form1())
End Sub
End Class