WinForms WebBrowser control detect redirections - winforms

Is there any way to detect 302 redirects within the web browser control that I don't know of? Maybe some kind of magic with the underlying unmanaged control?

No, it's not possible with the normal WebBrowser control. Doing anything with the underlying unmanaged control looks very complex, if that's what you're after you could have a look at
http://www.codeproject.com/KB/miscctrl/csEXWB.aspx though:
csEXWB is a C# .NET 2.0 control that
creates, hosts and sinks the events of
the original Webbrowser control (Not
.NET or any other wrapper). Advanced
customization and total control over
the Webbrowser control are achieved
via implementation of a number of
interfaces, along with the addition of
many methods, properties, events and a
COM library.
That includes being able to view page headers, which is what you'd need to do to detect the HTTP redirect.

I was able to detect the redirect within the NavigateError event. The statuscode will be 301 or 302. I set a flag that a redirection is to occur. Then in the navigatecomplete2 event, the URL is the redirection URL. In that event handler I check my redirection flag, and if true, clear the redirect flag and navigate again to the new URL. This should work for multiple redirects. This is VB6 code, but should work in theory for any WebBrowser implementation.
Dim m_isRedirected as boolean
...
Private Sub WebBrowser1_NavigateComplete2(ByVal pDisp As Object, URL As Variant)
If m_isRedirected Then
m_isRedirected = False 'clear the flag in case another redirect occurs
WebBrowser1.Navigate2 URL
End If
End Sub
Private Sub WebBrowser1_NavigateError(ByVal pDisp As Object, URL As Variant, Frame As Variant, StatusCode As Variant, Cancel As Boolean)
If StatusCode = 301 Or StatusCode = 302 Then m_isRedirected = True
End Sub

I found a nice workaround...
what you can do is capture/overload the webbrowser.OnNavigating and OnNavigated events.
Normally when navigating both the OnNavigating and OnNavigated events get called.
However whenever a redirect occurs the OnNavigated is called, but NOT the OnNavigating event.
so basically I'm using a counter that is reset in the OnNavigating event to 0.
in the OnNavigating i'm increasing the counter by 1.
in the OnDocumentCompleted event i will check the counter:
if counter == 1, no redirect occured
if counter> 1, redirects have occured.
so far this trick seems to work for me.

Using the WebBrowser control in WPF, you can look for the Navigating event. That won't give you the status codes, but you can see the URLs that the web browser is redirected to, so should be able to figure out when a redirection happens. I'm not that familiar with WinForms, but it may well work in a similar fashion.
private void Browser_Navigating(object sender, NavigatingCancelEventArgs e)
{
System.Diagnostics.Trace.WriteLine(e.Uri);
}

Related

Allow a user to override a default action via an event (eg use of e.handled or e.cancel = true or false)

I have a WPF Custom control which I have been building as I happen to need a particular functionality and I want to learn how to do this properly. Most of the control has now come together nicely and is generally pretty usable. There is now one feature that I'd like to implement, however, I'm not sure if it's possible in the first place or if it is how I would or should go about implementing it.
So here goes by way of my explanation of what I have and what I would like. Feel free to ask for extra clarification if required.
My control raises a custom event which passes along some custom event args. The event is declared like so in the control.
Public Event RecordControlButtonClicked(sender As Object, e As VtlDataNavigatorEventArgs)
In most cases when this event is raised the end user has simply clicked the button, nothing other than raising the event will take place because the developer using the control will most likely want to decide how they handle the situation at that particular moment and the custom event args provide more than enough info to do that.
There are however a couple of buttons where in all honesty the developer will probably want to happen what I (as the developer of the control) envisage should happen most of the time but on the odd occasion might not and therefore needs the opportunity to cancel out of it. So what I'm wondering is how I might achieve the following:
The end user clicks a button
The control button raises my custom event
The developer using my custom controls decides they wish to ignore what I (the creator of the control) thinks should happen, so they do something like e.handled = true in the code where they handle my custom event.
Somehow that message gets back to the button that raised the event, it seems that it's been 'handled' or 'canceled' and as a result does nothing
My question is how do I create that sort of functionality and is the event that I currently have declared the correct sort of event to handle this?
Well just in case anyone stumbles across this looking for an answer.
It transpires that my basic mistake lay in the way that I'd Created my custom events class. Originally I'd followed several examples that I'd read about and had my custom events inherit from EventArgs;
Public Class MyCustomEventArgs
Inherits EventArgs
End Class
What I should have done was have it inherit from CancelEventArgs;
Public Class MyCustomEventArgs
Inherits System.ComponentModel.CancelEventArgs
End Class
That provides the Property that I was after. Now when the event is actually handled upstreap the developer can add an e.cancel = true and all I need to do back in the originating call to raise the event is listen out for e.cancel ;
RaiseEvent MyCustomEvent(sender, e)
if e.cancel = true
'presumably don't do something
Else
'do what needs to be done
End If

Passing Parameters from one view model to another in wpf

As I continue to adapt to the new world of wpf and mvvm I find myself with another problem which is proving to be a little vexing.
In windows forms if I needed a new form to take a parameter I simply passed the information that I needed to that form's constructor. As Winforms is essentially a code behind model anyway this wasn't an issue.
I now find myself with a MainWindow (controlled by MainWindowViewModel) and a Login (controlled by LoginViewModel) which is shown as a dialog first. I have sorted the issue of the missing dialog result and I can get the main window to show after closing the login dialog.
The login dialog is shown and responded to in the Application startup with the following code;
dim dlg As New Login
If dlg.ShowDialog() = True Then
Else
Current.Shutdown(0)
End If
and I use the use the following in the code behind of the login window to take care of showing the main window when the login dialog closes;
Public Sub New
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Application.Current.ShutdownMode =ShutdownMode.OnExplicitShutdown
End Sub
Protected Overrides Sub OnClosed(e As EventArgs)
MyBase.OnClosed(e)
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose
End Sub
Now the Mainwindow already knows that it's datacontext is MainWindoViewModel because I set that up when designing it with blend, so how do I pass any parameters that I want to pass from the LoginViewModel to the MainWindowViewModel?
I can read c# but would prefer vb if possible. I know that this is something that I will want to be doing often so if there is a generic way to do this then that's the way I'd like to go.
Thanks
Mvvm frameworks generally implement some kind of messaging system to allow data to be moved between viewmodels or sometimes between viewmodel and view.
You can look at the way messaging is handled in some of the more popular frameworks such as MvvmLight or MvvmCross. It's usually just like an event that uses a weak reference to prevent tight coupling, so you can register for a type of message and then from another viewmodel you publish a message to any registered listeners.
So you will either need to implement your own kind of messenger, or now might be a time to look at starting to use a third party framework, unless you have specific reasons for avoiding this.

WPF fail. Using a WPF page as an instance

Think about a WPF program that has 2 pages which you navigate between. (Upload and Crop)
In the first page, you load an image using a button, select the image, and your image appears in the WPF Image control. Then, you press the "Crop" button. That sends the navigation to the second page, which you can crop the image.
Then, you crop the image in the second page. Then you press "next". Next button must redirect you to the first page, but wait a minute, the image you loaded no longer exist in the page. Because it's a new page.
Dim CropPage As New Crop(Crop_Bmp, Crop_BmpSource)
Me.NavigationService.Navigate(CropPage)
You may think it's okay. Because we pass the parameters and we can refill the WPF Image controls using that parameters. But, there is actually 8 upload panels in the page 1. So, other ones are important.
Or think about another method I found. Instead of using a new page instance, navigate back that you used.
Upload.Crop_Bmp = Crop_Bmp
Upload.Crop_BmpSource = Crop_BmpSource
Me.NavigationService.GoBack()
Upload.Crop()
But in the second one, it throws errors.
"Reference to a non-shared member requires an object reference."
Then, back to the Upload page, I made the variables and functions "Shared". But then, more problems come up. Because you cannot edit the local variables unless you also make them Shared. So, all of the variables and functions need to be "Shared".
I'm searching about a solution for a few days and I could not get rid of them. What method should I use? Redirecting back, or opening a new instance?
In old-type applications, it was very easy. All was that:
Form1.Crop_Bmp = Crop_Bmp
Me.Hide()
Form1.Crop()
And it was working perfectly. Because the forms were not instances like WPF pages. That's the reason I find WPF quite weird.
I found it a way to do this. When navigating to the Crop page, I sent the Upload page as a parameter to Crop Page.
Dim CropPage As New Crop(Crop_Bmp, Crop_BmpSource, Me)
Me.NavigationService.Navigate(CropPage)
Then, in Crop:
Public Sub New(ByVal Crop_Bitmap_ As Bitmap, ByVal Crop_BitmapSource_ As BitmapSource, ByVal pg As Upload)
' This call is required by the designer.
InitializeComponent()
UploadPage = pg
Then, in the Crop page, I accessed that parameter to modify its elements. For example:
Me.NavigationService.Navigate(UploadPage)
UploadPage.Crop()
This sounds like a bad use-case for the Pages metaphor. Pages are for loosely coupled peer screens (E.G. A separate page for each department), but you are using them in a tightly coupled parent-child relationship. (The "Crop" page is the child of the "Upload" page) For what you are doing, launching the crop screen as modal dialog might be more fitting.
If you want to keep everything in the same window, you can keep both functions in the same window, and toggle their visibility as needed.

AccessViolation exception when form with AxWindowsMediaPlayer closed

I have a AxWMPLib.AxWindowsMediaPlayer on a form. When I close the form, I get "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." exception. It is OK with hiding the form but not with closing. Everything's fine when the component is removed from the form.
This is Winforms .Net3.5.
Any help appreciated.
This was happening to me, and it was when closing the form during a key press.
Seems the WMP control will cause problems if it has a key event to process.
Example with Form.KeyPreview = True
Sub Form_KeyDown(e As KeyEventArgs)
AxWindowsMediaPlayer1.Dispose()
End Sub
Causes an Access Violation.
Sub Form_KeyDown(e As KeyEventArgs)
e.Handled = True
AxWindowsMediaPlayer1.Dispose()
End Sub
Closes cleanly, as the key press is blocked from reaching the WMP control.
Same thing happens when the form is closed as will dispose of the control.
Sometimes when working with ActiveX objects in .NET applications it is necessary to force garbage collection on exit. I generally do this in Form_Closing using:
GC.WaitForPendingFinalizers()
GC.Collect()
Also, if you have setup any event handlers for the object, you will want to disconnect them explicitly. I've found on a number of occasions that ActiveX objects will still remain active in the garbage bin and will attempt to call the event handler even after they have been disposed.
It may also be worth it to make sure playback has stopped before you try to dispose of the object.
ActiveX objects may have some sensitive dependencies on being closed in the correct order when the parent form is closed, otherwise they may go on living until gc runs - try looking through the interface for the control for any methods that look like they may have to do with closing, or destroying the object and calling those.
I think I have it:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
axWindowsMediaPlayer1.close();
}
simply found the method on the doc http://msdn.microsoft.com/en-us/library/windows/desktop/dd562388(v=vs.85).aspx
I thought I'd give it a go. it seems much better now.

WPF Frame Source Refreshing the Loaded Page

I've come across a strange scenario where a frame refuses to refresh its content.
I can kinda understand what's happening but the solution is not coming to me.
I have a page that has a frame (Frame1) and several buttons. When I click on a button a page is loaded into the frame. This works perfectly in most situations.
Private Sub btnIncidents_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnIncidents.Click
Frame1.Source = New System.Uri("/Incident/Incidents.xaml", UriKind.Relative)
End Sub
However, I have a situation where I need to select the button that was previously clicked to effectively do a Refresh on the Uri. The problem is it simply does nothing because the Uri hasn't changed. That makes sense but it's not what I want, I need to call it again.
My first solution was to introduce Frame1.Refresh which initially did the trick. But once a page was Refreshed, none of the Buttons could load a different page.
It was as if by calling Frame1.Refresh I'd broken the Frames ability to navigate to other pages.
My second idea was to set the Frame source to Nothing (Null) and then set the source to the URI but that didn't work either. ie Frame1.Source = Nothing
Has anyone else come across this or maybe have some suggestions? I just need to Refresh/Reload the Frames page without breaking the Frame!
I had the similar problem - with html though. Resolved it with
frame.NavigationService.Refresh();
apparently will clear navigation history, but in my case I do not really care.
Got the answer from http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/8fc6bd83-2803-4820-a22b-d4d87638c4e2

Resources