This project has moved. For the latest updates, please go here.

[SOLVED]PropertyPage

Jun 20, 2013 at 6:33 PM
Hi,

I'm kind of stuck because I'm trying to do this :
http://social.msdn.microsoft.com/Forums/vstudio/en-US/f7dd6f82-13e7-4a28-8316-9c535eaa7518/customize-outlook-options-dialog

And as you can see I have to create a class that inherit from class "UserControl" and that implements "Outlook.PropertyPage". But this is an interface in the example (in Microsoft.Office.Interop.Outlook) but with NetOffice this interface became a class, so I couldn't inherit from both UserControl and PropertyPage.

How I'm supposed to do ?
Thanks for your help
Coordinator
Jun 20, 2013 at 7:27 PM
hello, this an VSTO example, i try to convert this to a native example at the weekend (VSTO is a library top of the standard Office COM interface(simplified)) NetOffice doesnt provide the PropertyPage as early-bind interface at the moment i see(issue)
please wait for the weekend, i change them as fast as possible and provide an example. Sebastian ________________________________
Jun 20, 2013 at 7:32 PM
Great!
Thank you very much
Jun 24, 2013 at 6:38 PM
You probably didn't got the time last week-end to work on it, but do you think you'll time next week-end ?
I'm sorry being pushy but I'd like to know because I have to end this project in 2 weeks.
Coordinator
Jun 29, 2013 at 3:53 AM
sorry for the delay. the propertypage interface is a scary one.
i create a custom property page exampe with NetOffice 1.6.0 and the new tools namespace.
its available in the download section.

Sebastian
Jun 29, 2013 at 2:32 PM
Edited Jun 29, 2013 at 2:42 PM
Great!
Now the interface works well, but I still have a problem.
The "isDirty" property doesn't seem to be called at anytime, neither the Apply method. Moreover, the button "Apply" is always disabled. When I click on "Ok" button the window is closed but the configuration is not saved.

Here : http://www.outlookcode.com/codedetail.aspx?id=978
They seems to say that we need to notify the parent object but I cannot cast it into NetOffice object :
The COM object "System.__ComObject" cannot be cast to the class type "NetOffice.OutlookApi.PropertyPageSite"
I tried to replace the cast by "new Outlook.PropertyPageSite((COMObject)propertyPageSite)" at line 162 in the given example, but it fails too (cannot cast System.__ComObject into NetOffice.COMObject).

I'm not sure the example I found is a good one, as it use reflection and it's quite dirty but according to this : http://www.codeproject.com/Articles/8917/Adding-custom-property-pages-to-Outlook-2003-using it's mandatory

Therefore, I'm sure it's almost done, I just have to found a way to cast Combject into NetOffice.COMObject, and I'm sure you can help me with that :)


Edit: I just figured out how to do.
The solution is to do new Outlook.PropertyPageSite(_application, propertyPageSite) (line 162 in the given example)
with _application being your "Outlook.Application"

Thank you for your help :)
Coordinator
Jun 29, 2013 at 4:21 PM
not sure .. solved? lol
Jun 29, 2013 at 8:30 PM
Yes this is solved ;)

My explanation appears not to be clear, so here is the code as this is clearer.
      /// <summary>
        /// This represents a C# Propertypage Sample for .Net Outlook AddIns
        /// for use with VS2003 and VS2005 and VSTO Tools for Outlook.
        /// Implements the Outlook.PropertyPage Interface.
        /// <remarks>
        /// 1. Add a C# Usercontrol to your Project named "OptionsPage".
        /// 2. Add a Button to your Control named "button1".
        /// 3. See code below.
        /// </remarks>
        /// <example >
        /// void ThisApplication_OptionsPagesAdd(Microsoft.Office.Interop.Outlook.PropertyPages Pages)
        /// {
        ///    try
        ///    {
        ///        Pages.Add(new OptionPage(), "X4U Tools");
        ///    }
        ///    catch (System.Exception ex)
        ///    {
        ///        Trace.WriteLine (ex.Message);
        ///    }
        /// }
        /// </example>
        /// </summary>
        [ComVisible(true)]
        public partial class OptionPage : UserControl, Outlook.PropertyPage
        {
            #region Private Variables

            /// <summary>
            /// This is the parent PropertyPageSite Object.
            /// If we want to enable the Apply Button, we must call the
            /// OnStatusChange() and the PropertyPageSIte Object will check our Dirty Flag. 
            /// </summary>
            Outlook.PropertyPageSite _PropertyPageSite = null;

            /// <summary>
            /// This is our Statusvariable indicating that our Options has changed.
            /// </summary>
            bool _Dirty = false;

            #endregion

            #region Construction / Cleanup
            Outlook.Application _application;
            /// <summary>
            /// The Construction.
            /// At this point we have no Parent Object.
            /// </summary>
            public OptionPage(Outlook.Application application)
            {
                InitializeComponent();
                _application = application;
                // Register for the Load event.
                this.Load += new EventHandler(OptionPage_Load);
            }

            /// <summary>
            /// Eventhandler for the OptionPage_Load event.
            /// Here we can access our ParentObject.
            /// </summary>
            /// <param name="sender">This OptionPage Object.</param>
            /// <param name="e">The Arguments, not used.</param>
            void OptionPage_Load(object sender, EventArgs e)
            {
                // Load our Settings here
                LoadOptions();

                // Get our Parent PropertyPageSite Object and store it into Classvariable.
                _PropertyPageSite = GetPropertyPageSite();
            }

            #endregion

            #region PropertyPage Members

            /// <summary>
            /// This function is called when the user clicks "Apply" or "OK".
            /// </summary>
            public void Apply()
            {
                if (_Dirty)
                {
                    // Save Settings
                    SaveOptions();

                    // Reset Dirty Flag
                    OnDirty(false);
                }
            }

            /// <summary>
            /// The Dirty Property used form parent PropertyPageSite object.
            /// </summary>
            public bool Dirty
            {
                get { return _Dirty; }
            }

            /// <summary>
            /// The GetPageInfo function used form parent PropertyPageSite object,
            /// when user requests help.
            /// </summary>
            /// <param name="HelpFile">The name of the Helpfile.</param>
            /// <param name="HelpContext">The Index of the HelpContext requested.</param>
            public void GetPageInfo(ref string HelpFile, ref int HelpContext)
            {
                // TODO: Implement Helpfile
            }

            /// <summary>
            /// The Caption Property.
            /// Bugfix for the "unnamed" Error.
            /// </summary>
            [DispId(-518)]
            public string Caption
            {
                // Return the OptionsPage Caption here.
                get { return "Whatever you want"; }
            }

            #endregion

            #region Helper functions

            /// <summary>
            /// This function gets the parent PropertyPageSite Object using Reflection.
            /// Must be called in Load event.
            /// </summary>
            /// <returns>The parent PropertyPageSite Object</returns>
            Outlook.PropertyPageSite GetPropertyPageSite()
            {
                Type type = typeof(System.Object);
                string assembly = type.Assembly.CodeBase.Replace("mscorlib.dll", "System.Windows.Forms.dll");
                assembly = assembly.Replace("file:///", "");

                string assemblyName = System.Reflection.AssemblyName.GetAssemblyName(assembly).FullName;
                Type unsafeNativeMethods = Type.GetType(System.Reflection.Assembly.CreateQualifiedName(assemblyName, "System.Windows.Forms.UnsafeNativeMethods"));

                Type oleObj = unsafeNativeMethods.GetNestedType("IOleObject");
                System.Reflection.MethodInfo methodInfo = oleObj.GetMethod("GetClientSite");
                object propertyPageSite = methodInfo.Invoke(this, null);
                // The line below is specific to NetOffice
                return new Outlook.PropertyPageSite(_application, propertyPageSite);
            }

            /// <summary>
            /// This Function sets our Control Drity or Clean and
            /// calls our Parent to check the control state.
            /// </summary>
            /// <param name="isDirty"></param>
            void OnDirty(bool isDirty)
            {
                _Dirty = isDirty;

                // When this Method is called, the PageSite checks for Dirty Flag of all Optionspages.
                
                _PropertyPageSite.OnStatusChange();
            }

            #endregion

            #region Load / Save Settings

            /// <summary>
            /// Loads OptionSettions from DataStore
            /// </summary>
            void LoadOptions()
            {
                settingBox.Text = Properties.Settings.Default.settingName;
            }

            /// <summary>
            /// Save OptionSettings into DataStore
            /// </summary>
            void SaveOptions()
            {
                // do ... 
                Properties.Settings.Default.settingName = settingBox.Text;
                // ...  this for every seting
                Properties.Settings.Default.Save();
            }

            #endregion

            #region Eventhandlers for Controls
            // do ...
            private void settingBox_TextChanged(object sender, EventArgs e)
            {
                // This prevent firing OnDirty while loading
                if (settingBox.Text != Properties.Settings.Default.settingName)
                    OnDirty(true);
            }
            // ...  this for every seting. Be sure to actually set the event (double click on the box in Designer view)
            #endregion
        }