internal void ExtenderSetValue(IExtenderProvider provider, object component, object value, PropertyDescriptor notifyDesc)
        {
            if (provider != null)
            {
                ISite site = GetSite(component);
                IComponentChangeService changeService = null;
                object oldValue = null;

                // Announce that we are about to change this component
                //
                if (site != null)
                {
                    changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
                    Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
                }

                // Make sure that it is ok to send the onchange events
                //
                if (changeService != null)
                {
                    oldValue = ExtenderGetValue(provider, component);
                    try {
                        changeService.OnComponentChanging(component, notifyDesc);
                    }
                    catch (CheckoutException coEx) {
                        if (coEx == CheckoutException.Canceled)
                        {
                            return;
                        }
                        throw coEx;
                    }
                }

                provider = (IExtenderProvider)GetInvokee(componentClass, provider);

                if (SetMethodValue != null)
                {
                    SetMethodValue.Invoke(provider, new object[] { component, value });

                    // Now notify the change service that the change was successful.
                    //
                    if (changeService != null)
                    {
                        changeService.OnComponentChanged(component, notifyDesc, oldValue, value);
                    }
                }
            }
        }
        /// <summary>
        /// This will set value to be the new value of this property on the
        /// component by invoking the setXXX method on the component. If the
        /// value specified is invalid, the component should throw an exception
        /// which will be passed up. The component designer should design the
        /// property so that getXXX following a setXXX should return the value
        /// passed in if no exception was thrown in the setXXX call.
        /// </summary>
        public override void SetValue(object component, object value)
        {
            Debug.WriteLine($"[{Name}]: SetValue({component?.GetType().Name ?? "(null)"}, {value?.GetType().Name ?? "(null)"})");

            if (component != null)
            {
                ISite  site     = GetSite(component);
                object oldValue = null;

                object invokee = GetInvocationTarget(_componentClass, component);

                if (!IsReadOnly)
                {
                    IComponentChangeService changeService = null;

                    // Announce that we are about to change this component
                    if (site != null)
                    {
                        changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
                    }

                    // Make sure that it is ok to send the onchange events
                    if (changeService != null)
                    {
                        oldValue = GetMethodValue.Invoke(invokee, null);
                        try
                        {
                            changeService.OnComponentChanging(component, this);
                        }
                        catch (CheckoutException coEx)
                        {
                            if (coEx == CheckoutException.Canceled)
                            {
                                return;
                            }
                            throw;
                        }
                    }

                    try
                    {
                        try
                        {
                            SetMethodValue.Invoke(invokee, new object[] { value });
                            OnValueChanged(invokee, EventArgs.Empty);
                        }
                        catch (Exception t)
                        {
                            // Give ourselves a chance to unwind properly before rethrowing the exception.
                            //
                            value = oldValue;

                            // If there was a problem setting the controls property then we get:
                            // ArgumentException (from properties set method)
                            // ==> Becomes inner exception of TargetInvocationException
                            // ==> caught here

                            if (t is TargetInvocationException && t.InnerException != null)
                            {
                                // Propagate the original exception up
                                throw t.InnerException;
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }
                    finally
                    {
                        // Now notify the change service that the change was successful.
                        changeService?.OnComponentChanged(component, this, oldValue, value);
                    }
                }
            }
        }
        /// <include file='doc\ReflectPropertyDescriptor.uex' path='docs/doc[@for="ReflectPropertyDescriptor.SetValue"]/*' />
        /// <devdoc>
        ///     This will set value to be the new value of this property on the
        ///     component by invoking the setXXX method on the component.  If the
        ///     value specified is invalid, the component should throw an exception
        ///     which will be passed up.  The component designer should design the
        ///     property so that getXXX following a setXXX should return the value
        ///     passed in if no exception was thrown in the setXXX call.
        /// </devdoc>
        public override void SetValue(object component, object value)
        {
#if DEBUG
            if (PropDescUsageSwitch.TraceVerbose)
            {
                string compName = "(null)";
                string valName  = "(null)";

                if (component != null)
                {
                    compName = component.ToString();
                }
                if (value != null)
                {
                    valName = value.ToString();
                }

                Debug.WriteLine("[" + Name + "]: SetValue(" + compName + ", " + valName + ")");
            }
#endif
            if (component != null)
            {
                ISite site = GetSite(component);
                IComponentChangeService changeService = null;
                object oldValue = null;

                object invokee = GetInvokee(componentClass, component);

                Debug.Assert(!IsReadOnly, "SetValue attempted on read-only property [" + Name + "]");
                if (!IsReadOnly)
                {
                    // Announce that we are about to change this component
                    //
                    if (site != null)
                    {
                        changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
                        Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
                    }


                    // Make sure that it is ok to send the onchange events
                    //
                    if (changeService != null)
                    {
                        oldValue = GetMethodValue.Invoke(invokee, null);
                        try {
                            changeService.OnComponentChanging(component, this);
                        }
                        catch (CheckoutException coEx) {
                            if (coEx == CheckoutException.Canceled)
                            {
                                return;
                            }
                            throw coEx;
                        }
                    }

                    try {
                        try {
                            SetMethodValue.Invoke(invokee, new object[] { value });
                            OnValueChanged(invokee, EventArgs.Empty);
                        }
                        catch (Exception t) {
                            // Give ourselves a chance to unwind properly before rethrowing the exception (bug# 20221).
                            //
                            value = oldValue;

                            // If there was a problem setting the controls property then we get:
                            // ArgumentException (from properties set method)
                            // ==> Becomes inner exception of TargetInvocationException
                            // ==> caught here

                            if (t is TargetInvocationException && t.InnerException != null)
                            {
                                // Propagate the original exception up
                                throw t.InnerException;
                            }
                            else
                            {
                                throw t;
                            }
                        }
                    }
                    finally {
                        // Now notify the change service that the change was successful.
                        //
                        if (changeService != null)
                        {
                            changeService.OnComponentChanged(component, this, oldValue, value);
                        }
                    }
                }
            }
        }