I need to add WebBrowser
control to dynamically generated Window
in WPF application.
Basically when agent clicks on the link I need to open the webbrowser
window that has close (X) button disabled (or hidden) on some pages and not on others. Reason is they get redirected to 3rd party payment system that collects the payment and if they close browser window after collecting the payment but before they are redirected back to our system we do not get notification of payment being collected while 3rd party charges our customers.
Originally web browser window was opened in Windows.Form
, but I was unable to find a way to disable X on Form, so figured to switch to WPF Window since our app is WPF in the first place, so I introduced window and panel but now when dynamically adding WebBrowser
control to panel Children
it gives following error
Error cannot convert from 'Desktop.ViewModels.PaymentViewModel.NavigateToTakePaymentCommand.MyWebBrowser' to 'System.Windows.UIElement'
private void OpenBrowser(PaymentViewModel viewModel, Uri uri)
{
viewModel.BrowserWindow = new WithoutCloseButton();
viewModel.BrowserWindow.Closed += BrowserWindow_Closed;
var browser = new MyWebBrowser();
var stackPanel = new StackPanel { Orientation = System.Windows.Controls.Orientation.Vertical };
stackPanel.Children.Add(browser); // this bit fails
viewModel.BrowserWindow.Content = stackPanel;
...
public class WithoutCloseButton : Window
{
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x80000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
}
public class MyWebBrowser : System.Windows.Forms.WebBrowser
{
public static Guid IID_IHttpSecurity
= new Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b");
public static Guid IID_IWindowForBindingUI
= new Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b");
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int E_NOINTERFACE = unchecked((int)0x80004002);
public const int RPC_E_RETRY = unchecked((int)0x80010109);
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
return new MyWebBrowserSite(this);
}
class MyWebBrowserSite : WebBrowserSite, UCOMIServiceProvider,IHttpSecurity, IWindowForBindingUI
{
private MyWebBrowser myWebBrowser;
public MyWebBrowserSite(MyWebBrowser myWebBrowser) : base(myWebBrowser)
{
this.myWebBrowser = myWebBrowser;
}
public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
if (riid == IID_IHttpSecurity)
{
ppvObject = Marshal.GetComInterfaceForObject(this
, typeof(IHttpSecurity));
return S_OK;
}
if (riid == IID_IWindowForBindingUI)
{
ppvObject = Marshal.GetComInterfaceForObject(this
, typeof(IWindowForBindingUI));
return S_OK;
}
ppvObject = IntPtr.Zero;
return E_NOINTERFACE;
}
public int GetWindow(ref Guid rguidReason , ref IntPtr phwnd)
{
if (rguidReason == IID_IHttpSecurity || rguidReason == IID_IWindowForBindingUI)
{
phwnd = myWebBrowser.Handle;
return S_OK;
}
else
{
phwnd = IntPtr.Zero;
return S_FALSE;
}
}
public int OnSecurityProblem(uint dwProblem)
{
//ignore errors
//undocumented return code, does not work on IE6
return S_OK;
}
}
}
How to add System.Windows.Forms.WebBrowser
to System.Windows.Controls.StackPanel
? Is there like a wrapper on something?
To add Window
Form
controls
in WPF
you need WindowsFormsHost first. Add WindowsFormHost
to your StackPanel
and then add your webbrowser
to WindowsFormHost
. XAML
examples are below:
1.
<WindowsFormsHost>
<wf:MaskedTextBox x:Name="mtbDate" Mask="00/00/0000"/>
</WindowsFormsHost>
2.
<wfh:WindowsFormsHost Grid.Row="1" x:Name="wbFormHost" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" TabIndex="6">
<wf:WebBrowser x:Name="webBrowser" x:FieldModifier="public" Dock="Fill"/>
</wfh:WindowsFormsHost>
where wfh
is xmlns:wfh="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
With help from @Siderite Zackwehdex
I had to add reference to WindowsFormsIntegration
. Go to project and expand References
-> Add Reference
-> Assemblies
-> Framework
-> tick WindowsFormsIntegration
.
Then I had to change the following bits.
public class WithoutCloseButton : Window
{
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x80000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
//THIS IS NEW
public void HideButtons()
{
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
and then in OpenBrowser()
I had to wrap WebBrowser
in WindowsFormsHost
private void OpenBrowser(PaymentViewModel viewModel, Uri uri)
{
viewModel.BrowserWindow = new WithoutCloseButton();
viewModel.BrowserWindow.Closed += BrowserWindow_Closed;
var browser = new MyWebBrowser();
var stackPanel = new StackPanel { Orientation = System.Windows.Controls.Orientation.Vertical };
var formsHost = new WindowsFormsHost {Child = browser};
stackPanel.Children.Add(formsHost);
viewModel.BrowserWindow.Content = stackPanel;
//.... then
browser.Navigate("about:blank");
browser.DocumentCompleted += delegate(object obj, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.ToString() == "about:blank")
{
((MyWebBrowser)obj).Navigate(uri);
}
if (e.Url.ToString().ToLower().Contains("accepted"))
{
ViewModel.AuthCode = this.GetAuthToken();
ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted(new object(), null);
ViewModel.BrowserWindow.Close();
ViewModel.BrowserWindow = null;
}
//THIS IS NEW
if (e.Url.ToString().ToLower().Contains("payment/confirmation"))
{
viewModel.BrowserWindow.HideButtons();
}
};
User contributions licensed under CC BY-SA 3.0