Matas Vaitkevicius Matas Vaitkevicius - 1 month ago 22
C# Question

Adding System.Windows.Forms.WebBrowser to System.Windows.Controls.StackPanel cannot convert to System.Windows.UIElement

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?

Answer

With help from @Siderite Zackwehdex

I had to add reference to WindowsFormsIntegration. Go to project and expand References -> Add Reference -> Assemblies -> Framework -> tick WindowsFormsIntegration.

enter image description here

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();
    }
};
Comments