WebBrowser or WPF control content may not display correctly in an Office application when hosted inside a CustomTaskPane control

Si applica a: Office ProductsOutlook DevelopmentOutlook 2019

Symptoms


In some situations, the WebBrowser or WPF control content might not display or function correctly in an Office application when the control is hosted inside a CustomTaskPane control or Outlook FormRegion.

Cause


These issues might be caused either by the host application not returning focus to the HTML elements rendered inside your controls or by high DPI and DPI scaling in Office solutions as indicated in the Handle high DPI and DPI scaling within your Office solution article.

Workaround


To work around issues to do with high DPI and DPI scaling

Add a class named NativeImports to your code, as illustrated below, and add the following line to the ThisAddIn_Startup method:

NativeImports.SetThreadDpiHostingBehavior(NativeImports.DPI_HOSTING_BEHAVIOR.DPI_HOSTING_BEHAVIOR_MIXED);// ThisAddIn.csnamespace TaskPaneWorkaround{    public partial class ThisAddIn    {        private MyUserControl myUserControl1;        private Microsoft.Office.Tools.CustomTaskPane myCustomTaskPane;        private void ThisAddIn_Startup(object sender, System.EventArgs e)        {            // Workaround for the rendering issues to do with DPI            NativeImports.SetThreadDpiHostingBehavior(NativeImports.DPI_HOSTING_BEHAVIOR.DPI_HOSTING_BEHAVIOR_MIXED);            myUserControl1 = new MyUserControl();            myCustomTaskPane = this.CustomTaskPanes.Add(myUserControl1, "My Task Pane");            myCustomTaskPane.Visible = true;        }        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)        {        }        #region VSTO generated code        /// <summary>        /// Required method for Designer support - do not modify        /// the contents of this method with the code editor.        /// </summary>        private void InternalStartup()        {            this.Startup += new System.EventHandler(ThisAddIn_Startup);            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);        }                #endregion    }}// NativeImports.cs namespace TaskPaneWorkaround{    class NativeImports    {        internal enum DPI_HOSTING_BEHAVIOR        {            DPI_HOSTING_BEHAVIOR_INVALID = -1,            DPI_HOSTING_BEHAVIOR_DEFAULT = 0,            DPI_HOSTING_BEHAVIOR_MIXED = 1        };        [DllImport("user32.dll")]        internal static extern DPI_HOSTING_BEHAVIOR SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR value);        [DllImport("user32.dll", SetLastError = true)]        private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);    }} 

To work around issues to do with the host application not returning focus

Add an intermediary form in between the CustomTaskPane and the actual WebBrowser or WPF control.

For more information on adding a custom task pane to an application, see the article Add a custom task pane to an application.

For WPF rendering issues, the workaround consists of hosting a WPF UserControl inside a WindowsForm as described in the article Walkthrough: Hosting a 3-D WPF Composite Control in Windows Forms.

Here is an example of a user control called MyUserControl that implements this workaround:

  1. Add a UserControl to your add-in, named MyUserControl.
  2. Modify the source code of your UserControl and declare the Load, Paint, and Resize event handlers as indicated in the example below.
  3. Add a WindowsForm form to your project. Name it WorkaroundForm and declare the Load event handler.
  4. Add your WebBrowser control to WorkaroundForm as indicated in the example below.
  5. Create a CustomTaskPane and add an instance of MyUserControl to it.
// ThisAddIn.csnamespace TaskPaneWorkaround{    public partial class ThisAddIn    {        private MyUserControl myUserControl1;        private Microsoft.Office.Tools.CustomTaskPane myCustomTaskPane;        private void ThisAddIn_Startup(object sender, System.EventArgs e)        {            myUserControl1 = new MyUserControl();            myCustomTaskPane = this.CustomTaskPanes.Add(myUserControl1, "My Task Pane");            myCustomTaskPane.Visible = true;        }        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)        {        }        #region VSTO generated code        /// <summary>        /// Required method for Designer support - do not modify        /// the contents of this method with the code editor.        /// </summary>        private void InternalStartup()        {            this.Startup += new System.EventHandler(ThisAddIn_Startup);            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);        }                #endregion    }}// MyUserControl.csusing System;using System.Windows.Forms;using System.Runtime.InteropServices;namespace TaskPaneWorkaround{    public partial class MyUserControl : UserControl    {        bool isformdisplayed = false;        WorkaroundForm workaroundForm;        [DllImport("user32.dll", SetLastError = true)]        private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);        public MyUserControl()        {            this.SuspendLayout();            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;            this.Name = "MyUserControl";            this.Paint += new System.Windows.Forms.PaintEventHandler(this.MyUserControl_Paint);            this.Resize += new System.EventHandler(this.MyUserControl_Resize);            this.ResumeLayout(false);            this.Paint += MyUserControl_Paint;            this.Resize += MyUserControl_Resize;        }        private void MyUserControl_Load(object sender, System.EventArgs e)        {           this.Paint += MyUserControl_Paint;        }        private void MyUserControl_Paint(object sender, PaintEventArgs e)        {            if (!isformdisplayed)            {                this.SuspendLayout();                workaroundForm = new WorkaroundForm();                SetParent(workaroundForm.Handle, this.Handle);                workaroundForm.Dock = DockStyle.Fill;                workaroundForm.Width = Width;                workaroundForm.Height = Height;                workaroundForm.Show();                isformdisplayed = true;                this.ResumeLayout();            }        }        private void MyUserControl_Resize(object sender, EventArgs e)        {            if (isformdisplayed)            {                workaroundForm.Width = this.Width;                workaroundForm.Height = this.Height;            }        }    }}//WorkaroundForm.csusing System;using System.Drawing;using System.Windows.Forms;namespace TaskPaneWorkaround{    public partial class WorkaroundForm : Form    {        public WorkaroundForm()        {            this.SuspendLayout();            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;            this.ClientSize = new System.Drawing.Size(509, 602);            this.Name = "Form1";            this.Text = "Form1";            this.Load += new System.EventHandler(this.WorkaroundForm_Load);            this.ResumeLayout(false);        }        private void WorkaroundForm_Load(object sender, EventArgs e)        {            this.SuspendLayout();            this.Location = new Point(0, 0);            this.Dock = DockStyle.Fill;            this.FormBorderStyle = FormBorderStyle.None;            WebBrowser webBrowser = new WebBrowser();            this.Controls.Add(webBrowser);            webBrowser.Location = new Point(0, 0);            webBrowser.Dock = DockStyle.Fill;            this.ResumeLayout();            webBrowser.Focus();            webBrowser.Navigate(new System.Uri("https://bing.com"));        }    }}

 

More information


Most WebBrowser and WPF control rendering issues have been addressed by hotfixes. However, if you run into any problems where the content isn’t rendered correctly, focus is not returned to the HTML elements rendered inside the control, or certain key combinations don't work as expected, consider the workaround mentioned above.

A code sample illustrating these workarounds is available on GitHub.

Author: v-venum

Writer: catagh, v-todmc

Tech Reviewers: mhaque, catagh

Editor: v-todmc