I am trying to create an instance of Internet Explorer from a WPF application, load a saved local file, and do some further processing once the file is loaded. However, although the file is visible in the Internet Explorer window, the DocumentComplete event never fires:
'static field
Dim iex As ShDocVw.InternetExplorer
Public Sub DoStuff()
Dim path = "c:\test.htm"
iex = New SHDocVw.InternetExplorer
iex.Visible = True
AddHandler iex.DocumentComplete, Sub(o As Object, ByRef url As Object)
'This code is never executed
Dim i = 5
End Sub
iex.Navigate2(path)
End Sub
When I navigate to a non-local URL (e.g. http://www.google.com) the DocumentComplete event does fire.
The same behavior exists for the NavigateComplete2 event.
I tried using a class member method instead of a lambda expression (maybe the lambda expression is going out of scope once the method exits?) using both AddressOf and Handles, but that didn't help.
What do I have to do to have the DocumentComplete event fire?
(NB: The page has no frames.)
Update
This code is being used in a class library, and I therefore cannot use the WebBrowser control, as it cannot be instantiated in code.
As SimonMourier points out in the comments, a WebBrowser can be instantiated in code:
Dim wb = New WebBrowser
AddHandler wb.LoadCompleted, Sub(s, e)
Dim i = 5
End Sub
wb.Navigate(path)
Although the LoadCompleted event still doesn't fire, the Navigated event does, and it appears to be sufficient for my purposes. (Apparently the WebBrowser has to be visible in order for LoadCompleted to fire -- see here and here -- and since I am not using the WebBrowser in the context of a window, I don't think this is even possible in my case.)
You should use the out-of-the-box standard WebBrowser Control that ships with WPF (there is another one for Winforms apps). It has all the basic events directly supported.
Should you miss some Winforms feature like IsWebBrowserContextMenuEnabled or ScriptErrorsSuppressed, I suggest you refer to my answer in this question on SO: How to deactivate "right click" on WPF Webbrowser Control?
You only need interop in these special cases or if you need to get ahold on the native underlying IE's Document Object Model (DOM), interfaces like IHTMLDocument2, etc.
Instead of using ShDocVw.InternetExplorer you can use WebBrowser control provided by WPF:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<WebBrowser x:Name="webBrowser" Visibility="Visible" />
</Grid>
</Window>
Class MainWindow
Public Sub DoStuff()
Dim path = New Uri("c:\test.htm")
AddHandler webBrowser.LoadCompleted, Sub(sender As Object, e As System.Windows.Navigation.NavigationEventArgs)
Dim i = 5
End Sub
webBrowser.Navigate(path)
End Sub
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
DoStuff()
End Sub
End Class
There is no need to use WebBrowser Control to get this issue resolved.
I have also faced this issue and it happens due to the access privileges.
Run your application with Admin privileges and it will be fine.
To debug try running your Visual Studio as Administrator and then test, The DocumentComplete event will be fired.
Update1:
In case of non-admin application if you can manage to get the internet explorer started with Admin privileges then also you can work with it using your non-admin application.
Simple start a Internet Explorer process with admin privileges.
Then you can hook it using this code
For Each IE As InternetExplorer In New SHDocVw.ShellWindows
If IE.FullName.ToLower.Contains("iexplore") And IE.LocationURL <> "" Then
'Capture IE here
End If
Next
Related
I'm trying to write a function in VB.Net WPF application for retrieving document’s height in MS WebBrowser control. I can get this value only after some time, when page has been rendered. So, I tried:
Private Function GetHeight(ByVal htmlstring As String) As Integer
Dim wb As New WebBrowser 'Declare WebBrowser
wb.Width = 940 'set Width
wb.NavigateToString(htmlstring) 'Navigate to content
Do Until wb.IsLoaded 'Wait until page is rendered
Loop
'Get DOM Document
Dim doc As mshtml.HTMLDocument = wb.Document
'Get sought value
Dim RetVal As Integer = CInt(doc.body.getAttribute("scrollHeight").ToString)
doc = Nothing : wb.Dispose() : wb = Nothing 'Free variables
Return RetVal 'Return value
End Function
But calling such function causes application to freeze. What should I do? Do I need to implement Async and Await keywords, as well as Threading.Tasks and how to achieve this?
According to MSDN, IsLoaded is a framework element property that indicates whether or not the control has been loaded for presentation, not whether the WebBrowser control's web page has been loaded.
While I question why you would instantiate a browser and have it navigate to a page in a method called "GetHeight"... what I think you want to do here is subscribe to the LoadCompleted event: https://msdn.microsoft.com/en-us/library/system.windows.controls.webbrowser.loadcompleted(v=vs.110).aspx
Perhaps you can construct your webbrowser and have it navigate in a different method, and subscribe to the LoadCompleted event with your GetHeight method?
Edit I neglected to mention that because you declared your browser control outside of xaml, the control will never be loaded (and thus, navgiation won't work) because it's not in the visual tree of the WPF application. You'll have to either use a webbrowser already declared in xaml (which I would recommend), or add it programmatically with something like this:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="MainPanel">
<Button Click="Button_Click"> Clicky</Button>
</StackPanel>
Class MainWindow
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim wb As New WebBrowser 'Declare WebBrowser
wb.Width = 940 'set Width
Me.MainPanel.Children.Add(wb)
wb.NavigateToString("www.stackoverflow.com")
AddHandler wb.LoadCompleted, Sub(s, ee) DoSomething()
End Sub
Private Sub DoSomething()
MessageBox.Show("blah")
End Sub
End Class
So here's the setup. I have a standalone WPF application which I've built with VB. Contained in the application is two windows, MainWindow and SettingsWindow, which opens as a dialog and is called from a subprocedure in MainWindow.xaml.vb on the event of a clicked "Settings" button (I like working with code-behind much better than XAML if I can help it).
In SettingsWindow.xaml.vb, I have a subprocedure handling the event of clicking a close button in the window. It executes Me.Close(). The intent is to close SettingsWindow and return focus to MainWindow, but instead, the entire application terminates.
Additionally, clicking the close button in MainWindow closes MainWindow, but doesn't terminate the application in Visual Studio's debug mode, while closing SettingsWindow does.
When building the application and running it outside of Visual Studio (running the .exe itself in Windows), closing SettingsWindow returns to MainWindow as intended, but attempting to re-open SettingsWindow causes the entire application to crash.
I'm relatively new to Visual Studio, and I can post code as needed. Thanks ahead of time for the help.
First, here is my `Application.xaml.vb' file handling app startup:
Class Application
Public Shared initmain As MainWindow = New MainWindow()
Public Shared Sub AppStart() Handles Me.Startup
initmain.UpdateSettings()
initmain.Show()
End Sub
End Class
The code above checks My.Settings, which works fine, then calls MainWindow open.
Here is the code in the MainWindow sub calling SettingsWindow:
Private Sub SettingStart() Handles SettingsButton.MouseUp
Dim SettingWin As SettingsWindow = New SettingsWindow()
' "If" blocks here that check My.Settings before opening the window
SettingWin.ShowDialog()
End Sub
Finally, here is the code that closes SettingsWindow:
Private Sub CloseButtonClick() Handles CloseButton.MouseUp
Me.Close()
End Sub
If you need it, here is Application.xaml:
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="AppStart">
<Application.Resources>
</Application.Resources>
</Application>
Update:
It looks like the issue has to do with the fact that the instance of MainWindow in Application.xaml.vb is Public Shared, but I need to be able to call to that instance from other places to update settings in realtime, so I can't change this. Workarounds?
Additionally, the program now functions properly outside of Visual Studio, no longer crashing.
You need to set the ShutdownMode="OnLastWindowClose" in Application.xaml
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
ShutdownMode="OnLastWindowClose" >
<Application.Resources>
</Application.Resources>
</Application>
MainWindow.xaml.vb
Class MainWindow
Private Sub ButtonSetting_Click(sender As Object, e As RoutedEventArgs) Handles ButtonSetting.Click
Dim SettingWin As SettingWindow = New SettingWindow()
Dim rslt As Boolean = False
rslt = SettingWin.ShowDialog()
End Sub
End Class
I'm just starting to learn VB and Visual Studio and I've run across a problem. I've spent the best part of a day trying to find the answer and I have a horrible feeling that it's going to be something very simple that I've over looked.
I'm working on a WPF in Visual Studio 2010 and am trying to dynamically create a button on the main window when a button is clicked (I know, everything I've read tells me this is pretty basic!) Here's an edited snippet of the code I've written:
Imports System.Data.OleDb
Imports System.Windows.Forms
Imports Excel = Microsoft.Office.Interop.Excel
Class MainWindow
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles edit.Click
...
Dim newButton As New Button
newButton.Text = "New Button"
newButton.Top = 200
newButton.Left = 20
Me.Controls.Add(newButton)
...
End Sub
To my eyes, this looks perfectly simple and correct, but I'm getting an error:
"'Controls' is not a member of 'myApp.MainWindow'."
Has anybody come across this before or know what the problem is? Apologies if this does turn out to be a simple fix :)
The error you are getting is telling you that Controls does not exist within MainWindow. Basically, there is not property by that name accessible from your event handler. If you are working with WPF and MainWindow inherits Window, then you need to set something within the Content property.
The best way to go about this, would be to have some form of container control as the content of the window. You can define this in XAML or in code (via code you should set the Window.Content property). Then, you can add more controls to that container. Suggested containers are Grid, Canvas and StackPanel, etc.
I would suggest something like this:
XAML
<MainWindow ...>
<StackPanel x:Name="ControlContainer">
<Button Content="Click me to create buttons!" Click="CreateButton_Click" />
</StackPanel>
</MainWindow>
Code Behind
Private Sub CreateButton_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim button As New Button()
' Initialize the button
' ...
' Add the button to the stack panel
Me.ControlContainer.Children.Add(button)
End Sub
well MainWindow is not a form. create a new project and copy the same code in a button it will work.
you must find out what is the problem with your form control.
It looks like you are mixing WinForms and WPF coding - these are two different technologies.
This link may help you to add a button at runtime in WPF
I have a WPF application with form that has a textbox named "txtStatusWindow". I also have a vb.net class handed to me by a co-worker that needs to be called and executed by the code in my application code-behind. My co-worker insists that I will need to use common .net events to update the textbox on my form.
The separate vb.net class:
Public Class globalclass
Public Event txtStatusWindow(ByVal Text As String)
Public Sub InitializeProgram()
RaiseEvent txtStatusWindow("Updating something.")
System.Threading.Thread.Sleep(2000)
RaiseEvent txtStatusWindow("Updating something else.")
System.Threading.Thread.Sleep(2000)
RaiseEvent txtStatusWindow("Updating something other than else.")
System.Threading.Thread.Sleep(2000)
RaiseEvent txtStatusWindow("Updating something other than the else stuff.")
System.Threading.Thread.Sleep(2000)
End Sub
End Class
I need to be able to call the sub "InitializeProgram()" from my code-behind, and it needs to be able to update "txtStatusWindow.text" as it runs.
I told him that the updating of the text box can be done with data-binding, but I don't know how to integrate a separate class like this into my project, how to call methods in it, or how to cause it to update my text blocks through data binding.
I also suggested that the methods in this class aren't optimal for connecting to the WPF project anyway, but he just wrote it as an example to discover how to connect the two projects.
Eventually, I will need to integrate classes like these that will be running separate threads to update their data from a dynamic source, and cause many controls to update in my application.
So far, the only way we have been able to get this to work from my code-behind is this:
Partial Public Class SplashScreen
Dim NewText as String
Public WithEvents Globals As globalclass = New globalclass
Public Delegate Sub StringDelegate(ByVal Text As String)
Public SplashText As String
Public Sub New()
MyBase.New()
Me.InitializeComponent()
Me.Show()
Globals.InitializeProgram()
End Sub
Public Sub UpdateSplashscreenHandler(ByVal Text As String) Handles Globals.UpdateSplashScreen
StatusWindowText.Text = Text
End Sub
Notwithstanding the fact that the WPF screen "freezes" until the "globalclass InitializeProgram" method completes (txtStatusWindow.Text does not update while sub without using the esoteric "refresh" extension...), I fully believe we are going about this the wrong way.
There are precious few examples out there concerning the integration and then binding to objects in existing code. Thanks for examining our little quandary.
If this status window is in XAML and the status window is a UserControl, then add a StatusText dependency property to the status window. Then, in the XAML you can bind to the value of that property with something like:
<UserControl x:Name="MyStatusWindow" ...>
<TextBlock Text="{Binding Path=StatusText, ElementName=MyStatusWindow}" />
</UserControl>
Then, from your event, just update the value of that StatusText property.
(Is that even close to what you were asking?)
Also, about that freezing: Instead of doing that updating in the constructor of that class, you might want to do it from the Loaded event of that control. It will still be freezing, though, unless you move it to a separate thread. Right now, that's happening on the same thread that the UI message pump is running on. This is the Dispatcher for that UI.
I have a problem with a solution that I'm trying to develope. This is my scenario:
I have a VB6 application and I would call from this application some WPF windows. I've used the Interop Form Library to define a WinForm like a bridge from VB6 to WPF. The Interop WinForm exposes the methods to start and shutdown the wpf application.
To do that, in the WPF Application I've defined an helper like this:
Public Class StartApplicationHelper
Public Shared Property IsReady As Boolean = False
Public Shared Event NotifyEvent As ValueEnterEventHandler
Public Shared Sub Start()
If System.Windows.Application.Current Is Nothing Then
Try
Dim myApp As Application = New Application
myApp.ShutdownMode = ShutdownMode.OnExplicitShutdown
myApp.InitializeComponent()
IsReady= True
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End If
End Sub
Public Shared Sub Shutdown()
If System.Windows.Application.Current IsNot Nothing Then
System.Windows.Application.Current.Shutdown()
IsReady = False
End If
End Sub
Public Shared Sub DispatchEvent(ByVal eve As String)
If IsReady Then EventDispatcherService.DispatchEvent(eve, New EventDispatcherDataChildWin(String.Empty, Nothing, Nothing, False))
End Sub
Public Shared Sub DispatchResult(ByVal res As Object)
RaiseEvent NotifyEvent(Nothing, New ValueEnterEventArgs(res))
End Sub
End Class
the method DispatchEvent manage the execute of specific event like the opening of an application window.
For example, on winform I've wrote this statements:
MyWpfApp.StartApplicationHelper.Start()
Do While MyWpfApp.StartApplicationHelper.IsReady = False
System.Windows.Forms.Application.DoEvents()
Loop
MyWpfApp.StartApplicationHelper.DispatchEvent("OpenWin1")
in this way I can define an InteropFormMethod to open wpf window from VB6 across the Interop WinForm.
This solution seems work but I have a problem with a particular use case where the wpf application are stopped (shutdown) and then restarted (start). This is the displayed error message: "Cannot create more than one System.Windows.Application instance in the same AppDomain".
I'm trying to modify my helper to manage this case but I still have not found a solution. I would clean the AppDomain to restart wpf application.
How can I do? Can you help me?
LukePet, here is another related question: WPF used within a WinForms application, where to put Application resources?
Perhaps it gives you all the help you need.
The easiest option is just to keep the WPF application "running":
When you want to close the WPF app just close all the open WPF window and do any cleanup you want but don't call Shutdown.
Later, when the WPF part is "restarted" just open the main window without reinitializing the application.
Your other option is running the WPF app in another AppDomain and loading and unloading it when starting/stopping, this is more complicated, makes communication between the WPF part and the rest of the app difficult and, in my opinion, not worth it in this case.