none
Why can't a TabControl be added as a child to the UserControl which implements PropertyPage interface? RRS feed

  • Question


  • namespace MyOutlookAddIn { partial class MyPropertyPage { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); this.b_tabControl = new System.Windows.Forms.Button(); this.b_userControl = new System.Windows.Forms.Button(); this.tabControl1.SuspendLayout(); this.tabPage1.SuspendLayout(); this.SuspendLayout(); // // tabControl1 // this.tabControl1.Controls.Add(this.tabPage1); this.tabControl1.Location = new System.Drawing.Point(12, 21); this.tabControl1.Name = "tabControl1"; this.tabControl1.SelectedIndex = 0; this.tabControl1.Size = new System.Drawing.Size(338, 321); this.tabControl1.TabIndex = 5; // // tabPage1 // this.tabPage1.Controls.Add(this.b_tabControl); this.tabPage1.Location = new System.Drawing.Point(4, 22); this.tabPage1.Name = "tabPage1"; this.tabPage1.Padding = new System.Windows.Forms.Padding(3); this.tabPage1.Size = new System.Drawing.Size(330, 295); this.tabPage1.TabIndex = 0; this.tabPage1.Text = "tabPage1"; this.tabPage1.UseVisualStyleBackColor = true; // // b_tabControl // this.b_tabControl.Location = new System.Drawing.Point(14, 6); this.b_tabControl.Name = "b_tabControl"; this.b_tabControl.Size = new System.Drawing.Size(148, 23); this.b_tabControl.TabIndex = 1; this.b_tabControl.Text = "Crash Outlook..."; this.b_tabControl.UseVisualStyleBackColor = true; this.b_tabControl.Click += new System.EventHandler(this.b_tabControl_Click); // // b_userControl // this.b_userControl.Location = new System.Drawing.Point(30, 357); this.b_userControl.Name = "b_userControl"; this.b_userControl.Size = new System.Drawing.Size(148, 23); this.b_userControl.TabIndex = 6; this.b_userControl.Text = "Hello, World!"; this.b_userControl.UseVisualStyleBackColor = true; this.b_userControl.Click += new System.EventHandler(this.b_userControl_Click); // // MyPropertyPage // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.b_userControl); this.Controls.Add(this.tabControl1); this.Name = "MyPropertyPage"; this.Size = new System.Drawing.Size(364, 404); this.tabControl1.ResumeLayout(false); this.tabPage1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.Button b_tabControl; private System.Windows.Forms.Button b_userControl; } }


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    namespace MyOutlookAddIn
    {
        [ComVisible(true)]
        public partial class MyPropertyPage
            : UserControl, Microsoft.Office.Interop.Outlook.PropertyPage  
        {
            bool isDirty = false;
            const int captionDispID = -518;
            private Outlook.PropertyPageSite site;
            System.Windows.Forms.VisualStyles.VisualStyleRenderer renderer = null;
            public MyPropertyPage()
            {
                InitializeComponent();
            }
            protected override void OnLoad(EventArgs e)
            {
                Type type = typeof(UserControl);
                Type oleType = type.Assembly.GetType("System.Windows.Forms.UnsafeNativeMethods+IOleObject");
                if (oleType == null) throw new InvalidOperationException("Could not get 'System.Windows.Forms.UnsafeNativeMethods+IOleObject'.");
                System.Reflection.MethodInfo method = oleType.GetMethod("GetClientSite");
                if (method == null) throw new InvalidOperationException("Could not get method 'IOleObject.GetClientSite'.");
                site = method.Invoke(this, null) as Outlook.PropertyPageSite;
                renderer = new System.Windows.Forms.VisualStyles.VisualStyleRenderer(System.Windows.Forms.VisualStyles.VisualStyleElement.Tab.Body.Normal); 
                base.OnLoad(e);
            }
            [DispId(captionDispID)]
            public string PageCaption
            {
                get
                {
                    return "My AddIn";
                }
            }
            #region PropertyPage Members
            void Outlook.PropertyPage.Apply()
            {
            }
            [Browsable(false)]
            public bool IsDirty
            {
                get { return isDirty; }
                set
                {
                    if (isDirty != value)
                    {
                        isDirty = value;
                        if (site != null) site.OnStatusChange();
                    }
                }
            }
            bool Outlook.PropertyPage.Dirty
            {
                get 
                {
                    return IsDirty;
                }
            }
            void Outlook.PropertyPage.GetPageInfo(ref string HelpFile, ref int HelpContext)
            {
            }
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                if (renderer != null)
                    renderer.DrawBackground(e.Graphics, this.Bounds, e.ClipRectangle);
            }
            #endregion
    
            private void b_tabControl_Click(object sender, EventArgs e)
            {
                MessageBox.Show("Farewell, cruel world...");
            }
    
            private void b_userControl_Click(object sender, EventArgs e)
            {
                MessageBox.Show("Hello, World!");
            }
        }
    }
    

    (Apologies for the scrolling)


    This is a PropertyPage which can be added to the Outlook Options dialog, setup using code from Eric's Blog. 

    (Note the issue is not with Eric's code but with my additions).

    The class inherits from UserControl. In ThisAddIn.cs there is code to add MyPropertyPage to the Pages collection of Outlook (not shown here, check the link to Erics blog). Thus Outlook makes MyPropertyPage available as a TabPage when clicking "Tools"->"Options". All good so far.

    I designed a PropertyPage similar to this but ran out of room for all of the controls that I needed. I did what I normally do in that situation, I added a TabControl to the Design view. This seemed like a reasonable solution at the time, I got more space and the controls could be organised logically into different sections. I did not consider designing separate UserControl/PropertyPage classes in order to get more Pages in the Options dialog. Some of the controls had click handlers (e.g. button, LinkLabel) and in these handlers I had calls to MessageBox.Show or Form.Show or Form.ShowDialog. However now I ran into trouble while testing in Outlook. Any code which opened another form/dialog/MessageBox would freeze the Outlook application including the Options dialog and whichever additional dialog I had just opened. This was not a modality/topmost issue, the entire application and all windows were frozen and Options would switch to "Options (Not Responding)". I naturally attempted to debug what might cause this to occur. The application would hang either at the call to Show() or ShowDialog() or sometimes at the end of the event handler function.

    This situation was naturally difficult to comprehend. Searching on news groups showed that other uses could call ShowDialog() without a problem from PropertyPage. Ken Slovak said that the first parameter should be "this" to avoid TopMost/modality issues (I hope I am quoting you accurately Ken). I also found some comments where others had had problems with ShowDialog() on PropertyPage but no resolution. The other postings indicated bewilderment at a seemingly simple UI task.

    I believe I have resolved my issue and am posting this to perhaps help others. I still cannot fully explain why this is happening as an "end-user" developer of .Net apps and suspect the answer lies deeper down in the Win32 layer.

    The accompanying code clearly demonstrates where I went wrong. If you read or run the code, you will see that within the UserControl I added another TabControl with TabPages. On the TabPage is a button with a Click handler with a MessageBox.Show(). Directly on the UserControl, not  on the TabControl, is another button with a Click handler and MessageBox.Show(). The TabPage button will freeze Outlook while the other button will not.

    I am going to stop using a TabControl on the UserControl but I am wondering if there is any fix for this? I found some circa 2000 C++ postings (not Office related) regarding CTabCntrl where the solution was to switch the focus to a control not on the CTabCntl prior to opening the dialog. I will try and test this myself.

    Sunday, April 1, 2012 9:46 PM

Answers

  • Hi Kim,

    Thanks for posting in the MSDN Forum.

    It's based on my experience that the Onload method is not necessary. I would recommend you comment it. This is a PropertyPage which I run on my side. It works fine on my side. I hope it can help you.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using System.Runtime.InteropServices;
    
    namespace OutlookAddIn3
    {
        [ComVisible(true )]
        public partial class UserControl1 : UserControl,Outlook.PropertyPage
        {
            const int captionDispID = -518;
            bool isDirty = false;
    
            public UserControl1()
            {
                InitializeComponent();
            }
    
            void Outlook.PropertyPage.Apply()
            {
                MessageBox.Show("PropertyPage will shown");
            }
    
            bool Outlook.PropertyPage.Dirty
            {
                get
                {
                    return isDirty;
                }
            }
    
            void Outlook.PropertyPage.GetPageInfo(ref string helpFile, ref int helpContext)
            {
    
            }
    
            [DispId(captionDispID)]
            public string PageCaption
            {
                get
                {
                    return "My First Property Page";
                }
            }
    
            private void tabControl1_Selected(object sender, TabControlEventArgs e)
            {
                MessageBox.Show(tabControl1.SelectedTab.Name);
            }
        }
    }

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, April 3, 2012 7:37 AM
    Moderator

All replies

  • I just added a TextBox directly to the UserControl (not the TabPage) and added MyTextBox.Focus(); immediately prior to MessageBox.Show("") and it worked! No freeze/hang in Outlook.

    I got this idea from http://forums.devx.com/archive/index.php/t-144774.html

    So is this the same issue from Win32 resurfacing 7 years later in dotNet?

    Sunday, April 1, 2012 9:54 PM
  • Hi Kim,

    Thanks for posting in the MSDN Forum.

    It's based on my experience that the Onload method is not necessary. I would recommend you comment it. This is a PropertyPage which I run on my side. It works fine on my side. I hope it can help you.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using System.Runtime.InteropServices;
    
    namespace OutlookAddIn3
    {
        [ComVisible(true )]
        public partial class UserControl1 : UserControl,Outlook.PropertyPage
        {
            const int captionDispID = -518;
            bool isDirty = false;
    
            public UserControl1()
            {
                InitializeComponent();
            }
    
            void Outlook.PropertyPage.Apply()
            {
                MessageBox.Show("PropertyPage will shown");
            }
    
            bool Outlook.PropertyPage.Dirty
            {
                get
                {
                    return isDirty;
                }
            }
    
            void Outlook.PropertyPage.GetPageInfo(ref string helpFile, ref int helpContext)
            {
    
            }
    
            [DispId(captionDispID)]
            public string PageCaption
            {
                get
                {
                    return "My First Property Page";
                }
            }
    
            private void tabControl1_Selected(object sender, TabControlEventArgs e)
            {
                MessageBox.Show(tabControl1.SelectedTab.Name);
            }
        }
    }

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, April 3, 2012 7:37 AM
    Moderator