当在 CustomTaskPane 控件中托管时,WebBrowser 或 WPF 控件在 Office 应用程序中无法正确显示内容

原始 KB 编号: 4490421

症状

在某些情况下,当控件托管在 CustomTaskPane 控件或 Outlook FormRegion 中时,WebBrowser 或 WPF 控件内容可能无法在 Microsoft Office 应用程序中显示或正常运行。

原因

这些问题可能是由主机应用程序未将焦点返回到控件内呈现的 HTML 元素,或者由 Microsoft Office 解决方案中的高 DPI 和 DPI 缩放导致,如处理 Microsoft Office 解决方案中的高 DPI 和 DPI 缩放一文中所述。

解决方法 1:高 DPI 和 DPI 缩放问题

将名为 的 NativeImports 类添加到代码中,如下所示,并将以下行添加到 ThisAddIn_Startup 方法:

NativeImports.SetThreadDpiHostingBehavior(NativeImports.DPI_HOSTING_BEHAVIOR.DPI_HOSTING_BEHAVIOR_MIXED);

// ThisAddIn.cs

namespace 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);
    }

}

解决方法 2:主机应用程序未返回焦点的问题

在 CustomTaskPane 与实际 WebBrowser 或 WPF 控件之间添加中间窗体。

有关向应用程序添加自定义任务窗格的详细信息,请参阅 向应用程序添加自定义任务窗格

对于 WPF 呈现问题,解决方法包括在 WindowsForm 中托管 WPF UserControl,如演练:在 Windows 窗体 中托管 3D WPF 复合控件中所述。

下面是实现此解决方法的名为 MyUserControl 的用户控件的示例:

  1. 将名为 MyUserControl的 UserControl 添加到外接程序。

  2. 修改 UserControl 的源代码,并声明 Load、Paint 和 Resize 事件处理程序,如以下示例所示。

  3. Windows 窗体 添加到项目。 将其命名 为 WorkaroundForm 并声明 Load 事件处理程序。

  4. WebBrowser 控件添加到 WorkaroundForm ,如以下示例所示。

  5. 创建 CustomTaskPane 并向其添加 MyUserControl 的实例。

    // ThisAddIn.cs
    
    namespace 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.cs
    
    using 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.cs
    
    using 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"));
            }
    
        }
    }
    

更多信息

修补程序解决了大多数 WebBrowser 和 WPF 控件呈现问题。 但是,如果遇到无法正确呈现内容、焦点未返回到控件中呈现的 HTML 元素或某些组合键无法按预期工作的问题,请考虑上述解决方法。

GitHub 上提供了说明解决方法的代码示例。