File: UI\MobileControls\Design\StyleSheetDesigner.cs
Project: ndp\fx\src\mit\System\Web\System.Web.Mobile.csproj (System.Web.Mobile)
//------------------------------------------------------------------------------
// <copyright file="StyleSheetDesigner.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Web.UI.Design.MobileControls
{
    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Diagnostics;
    using System.Drawing.Design;
    using System.Globalization;
    using System.Text;
    using System.Web.UI;
    using System.Web.UI.Design;
    using System.Web.UI.Design.MobileControls.Adapters;
    using System.Web.UI.Design.MobileControls.Converters;
    using System.Web.UI.Design.MobileControls.Util;
    using System.Web.UI.MobileControls;
    using System.Windows.Forms;
 
    using Control = System.Web.UI.Control;
    using DataBindingCollectionEditor = System.Web.UI.Design.DataBindingCollectionEditor;
 
    [
        System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand,
        Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
    ]
    [Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
    internal class StyleSheetDesigner : MobileTemplatedControlDesigner, IDeviceSpecificDesigner
    {
        internal static BooleanSwitch StyleSheetDesignerSwitch =
            new BooleanSwitch("StyleSheetDesigner", "Enable StyleSheet designer general purpose traces.");
 
        private IWebFormsDocumentService _iWebFormsDocumentService;
        private IRefreshableDeviceSpecificEditor _deviceSpecificEditor;
        private DesignerVerbCollection _designerVerbs;
        private System.Web.UI.MobileControls.StyleSheet _styleSheet;
        private Style _currentStyle, _tmpCurrentStyle;
        private bool _isDuplicate;
        private MergedUI _mergedUI = null;
        private ArrayList _cycledStyles = null;
        private const int _templateWidth = 300;
        private static bool _requiresDesignTimeChanges = false;
        private bool _shouldRepersistStyles = false;
        private EventHandler _loadComplete = null;
 
        private const String _templatesStylePropName = "TemplateStyle";
        private const String _persistedStylesPropName = "PersistedStyles";
 
        private const String _designTimeHTML =
            @"
                <table cellpadding=4 cellspacing=0 width='300px' style='font-family:tahoma;font-size:8pt;color:buttontext;background-color:buttonface;border: solid 1px;border-top-color:buttonhighlight;border-left-color:buttonhighlight;border-bottom-color:buttonshadow;border-right-color:buttonshadow'>
                  <tr><td colspan=2><span style='font-weight:bold'>StyleSheet</span> - {0}</td></tr>
                  <tr><td style='padding-top:0;padding-bottom:0;width:55%;padding-left:10px;font-weight:bold'>Template Style:</td><td style='padding-top:0;padding-bottom:0'>{1}</td></tr>
                  <tr><td style='padding-top:0;padding-bottom:0;width:55%;padding-left:10px;font-weight:bold'>Template Device Filter:</td><td style='padding-top:0;padding-bottom:0'>{2}</td></tr>
                  <tr><td colspan=2 style='padding-top:4px'>{3}</td></tr>
                </table>
             ";
 
        private const String _specialCaseDesignTimeHTML =
            @"
                <table cellpadding=4 cellspacing=0 width='300px' style='font-family:tahoma;font-size:8pt;color:buttontext;background-color:buttonface;border: solid 1px;border-top-color:buttonhighlight;border-left-color:buttonhighlight;border-bottom-color:buttonshadow;border-right-color:buttonshadow'>
                  <tr><td colspan=2><span style='font-weight:bold'>StyleSheet</span> - {0}</td></tr>
                  <tr><td style='padding-top:0;padding-bottom:0;width:55%;padding-left:10px;font-weight:bold'>Template Style:</td><td style='padding-top:0;padding-bottom:0'>{1}</td></tr>
                  <tr><td style='padding-top:0;padding-bottom:0;width:55%;padding-left:10px;font-weight:bold'>Template Device Filter:</td><td style='padding-top:0;padding-bottom:0'>{2}</td></tr>
                  <tr><td colspan=2 style='padding-top:4px'>{3}</td></tr>
                  <tr><td colspan=2>
                    <table style='font-size:8pt;color:window;background-color:ButtonShadow'>
                      <tr><td valign='top'><img src='{4}'/></td><td>{5}</td></tr>
                    </table>
                  </td></tr>
                </table>
             ";
 
        private const int _headerFooterTemplates            = 0;
        private const int _itemTemplates                    = 1;
        private const int _separatorTemplate                = 2;
        private const int _contentTemplate                  = 3;
        private const int _numberOfTemplateFrames           = 4;
 
        private static readonly String[][] _templateFrameNames =
            new String[][] {
                               new String [] { Constants.HeaderTemplateTag, Constants.FooterTemplateTag },
                               new String [] { Constants.ItemTemplateTag, Constants.AlternatingItemTemplateTag, Constants.ItemDetailsTemplateTag },
                               new String [] { Constants.SeparatorTemplateTag },
                               new String [] { Constants.ContentTemplateTag }
                           };
 
        private const String _templateStyle = "__TemplateStyle__";
 
        // used by DesignerAdapterUtil.GetMaxWidthToFit
        // and needs to be exposed in object model because
        // custom controls may need to access the value just like
        // DesignerAdapterUtil.GetMaxWidthToFit does.
        public override int TemplateWidth
        {
            get
            {
                return _templateWidth;
            }
        }
 
        private MobilePage MobilePage
        {
            get
            {
                IComponent component = DesignerAdapterUtil.GetRootComponent(Component);
                if (component is MobileUserControl)
                {
                    return ((Control)component).Page as MobilePage;
                }
                return component as MobilePage;
            }
        }
 
        private Control RootControl
        {
            get
            {
                IComponent component = DesignerAdapterUtil.GetRootComponent(Component);
                return component as Control;
            }
        }
 
        /// <summary>
        ///    <para>
        ///       Initializes the designer.
        ///    </para>
        /// </summary>
        /// <param name='component'>
        ///    The control element being designed.
        /// </param>
        /// <remarks>
        ///    <para>
        ///       This is called by the designer host to establish the component being
        ///       designed.
        ///    </para>
        /// </remarks>
        /// <seealso cref='System.ComponentModel.Design.IDesigner'/>
        public override void Initialize(IComponent component)
        {
            Debug.Assert(component is System.Web.UI.MobileControls.StyleSheet,
                         "StyleSheetDesigner.Initialize - Invalid StyleSheet Control");
            base.Initialize(component);
 
            _isDuplicate = false;
            _styleSheet = (System.Web.UI.MobileControls.StyleSheet) component;
            if(_requiresDesignTimeChanges)
            {
                _shouldRepersistStyles = true;
            }
            _loadComplete = new EventHandler(this.OnLoadComplete);
            IWebFormsDocumentService.LoadComplete += _loadComplete;
 
            if (IMobileWebFormServices != null)
            {
                TemplateStyle = (String) IMobileWebFormServices.GetCache(_styleSheet.ID, _templateStyle);
                TemplateDeviceFilter = 
                    (String) IMobileWebFormServices.GetCache(
                    _styleSheet.ID,
                    MobileTemplatedControlDesigner.DefaultTemplateDeviceFilter);
            }
        }
 
        private void OnLoadComplete(Object source, EventArgs e) 
        {
            if(_shouldRepersistStyles)
            {
                IsDirty = true;
                OnInternalChange();
            }
            _requiresDesignTimeChanges = false;
            _shouldRepersistStyles = false;
 
            UpdateDesignTimeHtml();
        }
        
        internal static void SetRequiresDesignTimeChanges()
        {
            _requiresDesignTimeChanges = true;
        }
 
        private IWebFormsDocumentService IWebFormsDocumentService
        {
            get
            {
                if (_iWebFormsDocumentService == null)
                {
                    _iWebFormsDocumentService =
                        (IWebFormsDocumentService)GetService(typeof(IWebFormsDocumentService));
 
                    Debug.Assert(_iWebFormsDocumentService != null);
                }
 
                return _iWebFormsDocumentService;
            }
        }
        
        protected override ITemplateEditingFrame CreateTemplateEditingFrame(TemplateEditingVerb verb)
        {
            ITemplateEditingService teService = 
                (ITemplateEditingService)GetService(typeof(ITemplateEditingService));
            Debug.Assert(teService != null,
                "How did we get this far without an ITemplateEditingService");
 
            String[] templateNames = GetTemplateFrameNames(verb.Index);
            ITemplateEditingFrame editingFrame = teService.CreateFrame(
                this, 
                TemplateDeviceFilter + " (" + TemplateStyle + ")",
                templateNames,
                WebCtrlStyle,
                null /* we don't have template styles */);
 
            editingFrame.InitialWidth = _templateWidth;
            return editingFrame;
        }
 
        protected override void Dispose(bool disposing) 
        {
            if (disposing) 
            {
                UpdateActiveStyleSheet();
                
                if (_loadComplete != null)
                {
                    IWebFormsDocumentService.LoadComplete -= _loadComplete;
                    _loadComplete = null;
                }
 
                if (IMobileWebFormServices != null)
                {
                    // If the page is in loading mode, it means the remove is trigged by webformdesigner.
                    if (!LoadComplete)
                    {
                        IMobileWebFormServices.SetCache(_styleSheet.ID, (Object) _templateStyle, (Object) this.TemplateStyle);
                    }
                    else
                    {
                        // setting to null will remove the entry.
                        IMobileWebFormServices.SetCache(_styleSheet.ID, (Object) _templateStyle, null);
                    }
                }
            }
 
            base.Dispose(disposing);
        }
 
        private void UpdateActiveStyleSheet()
        {
            if (MobilePage != null && MobilePage.StyleSheet == _styleSheet)
            {
                IDesigner designer = null;
 
                // currently active stylesheet is deleted
                MobilePage.StyleSheet = StyleSheet.Default;
                StyleSheet _newStyleSheet = null;
 
                Debug.Assert(RootControl != null);
                foreach (Control control in RootControl.Controls)
                {
                    // Find new stylesheet
                    if (control is StyleSheet && _newStyleSheet == null && control != _styleSheet)
                    {
                        designer = Host.GetDesigner((IComponent) control);
                        // AUI 7285
                        if (designer != null)
                        {
                            _newStyleSheet = (StyleSheet) control;
                        }
                    }
                }
 
                MobilePage.StyleSheet = _newStyleSheet;
                if (null != _newStyleSheet)
                {
                    Debug.Assert(designer != null);
                    StyleSheetDesigner ssd = designer as StyleSheetDesigner;
                    Debug.Assert(ssd != null, "ssd is null in StyleSheetDesigner");
                    ssd.TreatAsDuplicate(false);
                }
                RefreshPageView();
            }
        }
        
        protected override String[] GetTemplateFrameNames(int index)
        {
            Debug.Assert(index >= 0 & index <= _templateFrameNames.Length);
            return _templateFrameNames[index];
        }
 
        protected override TemplateEditingVerb[] GetTemplateVerbs()
        {
            TemplateEditingVerb[] templateVerbs = new TemplateEditingVerb[_numberOfTemplateFrames];
 
            templateVerbs[_headerFooterTemplates] = new TemplateEditingVerb(
                SR.GetString(SR.TemplateFrame_HeaderFooterTemplates),
                _headerFooterTemplates,
                this);
            templateVerbs[_itemTemplates] = new TemplateEditingVerb(
                SR.GetString(SR.TemplateFrame_ItemTemplates),
                _itemTemplates,
                this);
            templateVerbs[_separatorTemplate] = new TemplateEditingVerb(
                SR.GetString(SR.TemplateFrame_SeparatorTemplate),
                _separatorTemplate,
                this);
            templateVerbs[_contentTemplate] = new TemplateEditingVerb(
                SR.GetString(SR.TemplateFrame_ContentTemplate),
                _contentTemplate,
                this);
 
            return templateVerbs;
        }
 
        /// <summary>
        ///    <para>
        ///       Delegate to handle component changed event.
        ///    </para>
        /// </summary>
        /// <param name='sender'>
        ///    The object sending the event.
        /// </param>
        /// <param name='ce'>
        ///    The event object used when firing a component changed notification.
        /// </param>
        /// <remarks>
        ///    <para>
        ///       This is called after a property has been changed. It allows the implementor
        ///       to do any post-processing that may be needed after a property change.
        ///    </para>
        /// </remarks>
        public override void OnComponentChanged(Object sender, ComponentChangedEventArgs ce) 
        {
            // Delegate to the base class implementation first!
            base.OnComponentChanged(sender, ce);
 
            MemberDescriptor member = ce.Member;
            if (member != null && 
                member.GetType().FullName.Equals(Constants.ReflectPropertyDescriptorTypeFullName))
            {
                PropertyDescriptor propDesc = (PropertyDescriptor)member;
                
                if (propDesc.Name.Equals("ID"))
                {
                    // Update the dictionary of device filters stored in the page designer
                    // setting to null will remove the entry.
                    IMobileWebFormServices.SetCache(ce.OldValue.ToString(), (Object) _templateStyle, null);
                }
            }
        }
 
        internal void OnStylesChanged()
        {
            // If this is not a new stylesheet and it is the current stylesheet
            if (MobilePage != null && MobilePage.StyleSheet == _styleSheet)
            {
                // Refresh the whole page assuming styles have been changed.
                RefreshPageView();
                ClearCycledStyles();
            }
        }
 
        private void RefreshPageView()
        {
            if (IMobileWebFormServices != null)
            {
                IMobileWebFormServices.RefreshPageView();
            }
        }
 
        public override void OnSetParent() 
        {
            base.OnSetParent();
 
            // This is not a MobilePage or the styleSheet is already the active styleSheet.
            // The latter happens when the active StyleSheet is drag/drop to another location
            // which forces its parent to be changed.
            if (MobilePage == null)
            {
                return;
            }
 
            if (MobilePage.StyleSheet == _styleSheet)
            {
                if (!(_styleSheet.Parent is MobilePage
                    || _styleSheet.Parent is MobileUserControl))
                {
                    UpdateActiveStyleSheet();
                }
                return;
            }
 
            if (MobilePage.StyleSheet != StyleSheet.Default) 
            {
                // can't accept more than 1 stylesheet
                TreatAsDuplicate(true);
 
                // the current valid StyleSheet is intentionaly refreshed because
                // if this stylesheet instance is recreated via a Undo operation
                // the current valid StyleSheet appears as a duplicate if not refreshed.
                IDesigner designer = Host.GetDesigner((IComponent) MobilePage.StyleSheet);
                Debug.Assert(designer != null, "designer is null in StyleSheetDesigner");
                StyleSheetDesigner ssd = (StyleSheetDesigner) designer;
                ssd.UpdateRendering();
            }
            else if (_styleSheet.Parent is MobilePage ||
                     _styleSheet.Parent is MobileUserControl)
            {
                // the active stylesheet is changed
                MobilePage.StyleSheet = _styleSheet;
                _isDuplicate = false;
            }
            RefreshPageView();
        }
 
        protected override void OnTemplateModeChanged() 
        {
            base.OnTemplateModeChanged();
 
            // Refresh all mobilecontrols after exit template editing mode.
            if (!InTemplateMode)
            {
                RefreshPageView();
            }        
        }
 
        public void TreatAsDuplicate(bool isDuplicate)
        {
            if (isDuplicate != _isDuplicate)
            {
                _isDuplicate = isDuplicate;
                SetTemplateVerbsDirty();
                UpdateDesignTimeHtml();
            }
        }
 
        protected override bool ErrorMode
        {
            get
            {
                return base.ErrorMode
                    || _isDuplicate
                    || _styleSheet.DuplicateStyles.Count > 0;
            }
        }
 
        private StringCollection GetDuplicateStyleNames()
        {
            StringCollection duplicateNamesList = new StringCollection();
 
            // Filter out repeated duplicate names using case insensitive
            // hash table
            HybridDictionary duplicateNamesHash = new HybridDictionary(
                true /* Names not case sensitive */ );
            foreach(Style style in _styleSheet.DuplicateStyles)
            {
                duplicateNamesHash[style.Name] = true;
            }
 
            // Copy remaining names into a string list
            foreach(DictionaryEntry entry in duplicateNamesHash)
            {
                duplicateNamesList.Add((String)entry.Key);
            }
            return duplicateNamesList;
        }
 
        protected override String GetDesignTimeNormalHtml()
        {
            String curStyle, message;
            ArrayList lstStylesInCycle = null;
 
            if (null == CurrentStyle)
            {
                curStyle = SR.GetString(SR.StyleSheet_PropNotSet);
            }
            else
            {
                curStyle = HttpUtility.HtmlEncode(CurrentStyle.Name);
            }
 
            String curChoice;
 
            if (null == CurrentChoice)
            {
                curChoice = SR.GetString(SR.StyleSheet_PropNotSet);
            }
            else
            {
                if (CurrentChoice.Filter.Length == 0)
                {
                    curChoice = SR.GetString(SR.DeviceFilter_DefaultChoice);
                }
                else
                {
                    curChoice = HttpUtility.HtmlEncode(DesignerUtility.ChoiceToUniqueIdentifier(CurrentChoice));
                }
            }
 
            message = SR.GetString(SR.StyleSheet_DefaultMessage);
 
            bool renderErrorMsg = false;
            String errorMsg = null;
            String errorIconUrl = null;
 
            if(_isDuplicate)
            {
                renderErrorMsg = true;
                errorMsg = SR.GetString(SR.StyleSheet_DuplicateWarningMessage);
                errorIconUrl = MobileControlDesigner.errorIcon;
            }
            else if(_styleSheet.DuplicateStyles.Count > 0)
            {
                renderErrorMsg = true;
                errorMsg = SR.GetString(
                    SR.StyleSheet_DuplicateStyleNamesMessage,
                    GenericUI.BuildCommaDelimitedList(
                        GetDuplicateStyleNames()
                    )
                );
                errorIconUrl = MobileControlDesigner.errorIcon;
            }
            else if (null != CurrentStyle && null != CurrentChoice)
            {
                if (IsHTMLSchema(CurrentChoice))
                {
                    message = SR.GetString(SR.StyleSheet_TemplateEditingMessage);
                }
                else
                {
                    // User has selected non-html schema
                    renderErrorMsg = true;
                    errorMsg = SR.GetString(SR.MobileControl_NonHtmlSchemaErrorMessage);
                    errorIconUrl = MobileControlDesigner.infoIcon;
                }
            }
 
            if (renderErrorMsg)
            {
                Debug.Assert(errorMsg != null && errorIconUrl != null);
                return String.Format(CultureInfo.CurrentCulture, _specialCaseDesignTimeHTML, 
                    new Object[]
                                     {
                                         _styleSheet.Site.Name,
                                         curStyle,
                                         curChoice,
                                         message,
                                         errorIconUrl,
                                         errorMsg
                                     });
            }
            else
            {
                lstStylesInCycle = DetectCycles();
 
                // 
 
                if (lstStylesInCycle != null && lstStylesInCycle.Count > 0)
                {
                    String cycledStyles = String.Empty;
                    // 
 
                    foreach (Object obj in lstStylesInCycle)
                    {
                        Style cycledStyle = (Style) obj;
                        if (cycledStyles.Length > 0)
                        {
                            cycledStyles += ", ";  
                        }
                        cycledStyles += cycledStyle.Name;
                    }
                    return String.Format(CultureInfo.CurrentCulture, _specialCaseDesignTimeHTML, 
                        new Object[]
                                         {
                                             _styleSheet.Site.Name,
                                             curStyle,
                                             curChoice,
                                             message,
                                             MobileControlDesigner.errorIcon,
                                             SR.GetString(SR.StyleSheet_RefCycleErrorMessage, cycledStyles)
                                         });
                }
                else
                {
                    return String.Format(CultureInfo.CurrentCulture, _designTimeHTML, 
                        new Object[]
                                         {
                                             _styleSheet.Site.Name,
                                             curStyle,
                                             curChoice,
                                             message
                                         });
                }
            }
        }
 
        private void ClearCycledStyles()
        {
            _cycledStyles = null;
        }
 
/* O(n) algorithm for loop detection
        private HybridDictionary DetectCycles()
        {
            if (_cycledStyles == null)
            {
                _cycledStyles = new HybridDictionary();
                ICollection styles = _styleSheet.Styles;
 
                // Initialize the set
                Hashtable styleSet = new Hashtable(styles.Count);
                foreach (String key in styles)
                {
                    styleSet.Add(key, true);
                }
 
                while (styleSet.Count > 0)
                {
                    Style style = null;
                    foreach (String key in styleSet.Keys)
                    {
                        style = (Style)_styleSheet[key];
                        Debug.Assert(style != null);
                        break;
                    }
 
                    int count = 0;
                    Traverse(styleSet, style, count);
                }
            }
            return _cycledStyles;
        }
        
        private bool Traverse(Hashtable styleSet, Style style)
        {
            String reference = style.StyleReference;
            Style nextStyle = null;
            bool result = false;
 
            styleSet.Remove(style.Name.ToLower(CultureInfo.InvariantCulture));
 
            if (reference == null || reference.Length == 0 || 
                ((nextStyle = (Style)_styleSheet[reference]) == null) ||
                (!styleSet.Contains(nextStyle)))
            {
                result = false;
            }
            else if (_cycledStyles.Contains(nextStyle) || 
                Traverse(styleSet, nextStyle, ++count))
            {
                Debug.Assert(_cycledStyles != null);
                if (!_cycledStyles.Contains(style))
                {
                    _cycledStyles.Add(style, "");
                }
                result = true;
            }
            
            return result;
        }
*/
        private ArrayList DetectCycles()
        {
            if (_cycledStyles == null)
            {
                _cycledStyles = new ArrayList();
                ICollection styles = _styleSheet.Styles;
 
                foreach (String key in styles)
                {
                    Style style = (Style) _styleSheet[key];
                    Style styleTmp;
                    Debug.Assert(style != null);
 
                    bool cycle = false;
                    String reference = style.StyleReference;
                    String name = style.Name;
 
                    int count = styles.Count + 1;
 
                    while ((reference != null && reference.Length > 0) && count > 0)
                    {
                        if (0 == String.Compare(name, reference, StringComparison.OrdinalIgnoreCase))
                        {
                            cycle = true;
                            break;
                        }
                        else
                        {
                            styleTmp = _styleSheet[reference];
                            if (null != styleTmp)
                            {
                                reference = styleTmp.StyleReference;
                                count --;
                            }
                            else
                            {
                                reference = null;
                            }
                        }
                    }
 
                    if (cycle)
                    {
                        _cycledStyles.Add(style);
                    }
                }
            }
 
            return _cycledStyles;
        }
 
        ////////////////////////////////////////////////////////////////////////
        //  Begin IDeviceSpecificDesigner Implementation
        ////////////////////////////////////////////////////////////////////////
 
        void IDeviceSpecificDesigner.SetDeviceSpecificEditor
            (IRefreshableDeviceSpecificEditor editor)
        {
            _deviceSpecificEditor = editor;
        }
 
        String IDeviceSpecificDesigner.CurrentDeviceSpecificID
        {
            get
            {
                if (_tmpCurrentStyle == null)
                {
                    return null;
                }
 
                if (_styleSheet[_tmpCurrentStyle.Name] == null)
                {
                    _tmpCurrentStyle = null;
                }
                return (_tmpCurrentStyle != null) ? _tmpCurrentStyle.Name.ToLower(CultureInfo.InvariantCulture) : null;
            }
        }
 
        System.Windows.Forms.Control IDeviceSpecificDesigner.Header
        {
            get
            {
                return _mergedUI;
            }
        }
 
        System.Web.UI.Control IDeviceSpecificDesigner.UnderlyingControl
        {
            get
            {
                return _styleSheet;
            }
        }
 
        Object IDeviceSpecificDesigner.UnderlyingObject
        {
            get
            {
                if (null != _mergedUI.CbStyles.SelectedItem)
                {
                    String styleName = (String) _mergedUI.CbStyles.SelectedItem;
                    return _styleSheet[styleName];
                }
                else
                {
                    return null;
                }
            }
        }
 
        bool IDeviceSpecificDesigner.GetDeviceSpecific(String deviceSpecificParentID, out DeviceSpecific ds)
        {
            Style style = (Style) _styleSheet[deviceSpecificParentID];
            if (null == style)
            {
                ds = null;
                return false;
            }
            else
            {
                ds = style.DeviceSpecific;
                return true;
            }
        }
 
        void IDeviceSpecificDesigner.SetDeviceSpecific(String deviceSpecificParentID, DeviceSpecific ds)
        {
            Style style = (Style) _styleSheet[deviceSpecificParentID];
            Debug.Assert(null != style, "style is null in IDeviceSpecificDesigner.SetDeviceSpecific");
            if (null != ds)
            {
                ds.SetOwner((MobileControl) _styleSheet);
            }
            style.DeviceSpecific = ds;
 
            if (CurrentChoice != null && 0 == String.Compare(CurrentStyle.Name, deviceSpecificParentID, StringComparison.OrdinalIgnoreCase))
            {
                if (ds == null)
                {
                    CurrentChoice = null;
                }
                else
                {
                    // This makes sure that the CurrentChoice value is set to null is
                    // it was deleted during the deviceSpecific object editing
                    if (CurrentChoice.Filter.Length == 0)
                    {
                        TemplateDeviceFilter = SR.GetString(SR.DeviceFilter_DefaultChoice);
                    }
                    else
                    {
                        TemplateDeviceFilter = DesignerUtility.ChoiceToUniqueIdentifier(CurrentChoice);
                    }
                }
            }
        }
 
        void IDeviceSpecificDesigner.InitHeader(int mergingContext)
        {
            _mergedUI = new MergedUI();
            _mergedUI.LblStyles.Text = SR.GetString(SR.StyleSheet_StylesCaption);
            _mergedUI.LblStyles.TabIndex = 1;
 
            _mergedUI.CbStyles.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            _mergedUI.CbStyles.SelectedIndexChanged += new EventHandler(this.OnSelectedIndexChangedStylesComboBox);
            _mergedUI.CbStyles.TabIndex = 2;
            _mergedUI.CbStyles.Sorted = true;
 
            _mergedUI.BtnEdit.Text = SR.GetString(SR.Stylesheet_EditBtnCaption);
            _mergedUI.BtnEdit.Click += new EventHandler(this.OnClickEditStylesButton);
            _mergedUI.BtnEdit.TabIndex = 3;
 
            switch (mergingContext)
            {
                case MobileControlDesigner.MergingContextTemplates:
                {
                    _mergedUI.LblHeader.Text = SR.GetString(SR.StyleSheet_SettingTemplatingStyleChoiceDescription);
 
                    // AUI 2730
                    _mergedUI.CbStyles.Width = 195;
                    _mergedUI.BtnEdit.Location = new System.Drawing.Point(201, 39);
                    break;
                }
 
                default:
                {
                    _mergedUI.LblHeader.Text = SR.GetString(SR.StyleSheet_SettingGenericStyleChoiceDescription);
 
                    // AUI 2730
                    _mergedUI.CbStyles.Width = 195;
                    _mergedUI.BtnEdit.Location = new System.Drawing.Point(201, 39);
                    break;
                }
            }
        }
 
        void IDeviceSpecificDesigner.RefreshHeader(int mergingContext)
        {
            _mergedUI.CbStyles.Items.Clear();
            ICollection styles = _styleSheet.Styles;
            foreach (String key in styles)
            {
                Style style = (Style) _styleSheet[key];
                Debug.Assert(style != null);
 
                _mergedUI.CbStyles.Items.Add(style.Name);
            }
 
            if (_mergedUI.CbStyles.Items.Count > 0)
            {
                Debug.Assert(null != CurrentStyle);
                _mergedUI.CbStyles.SelectedItem = CurrentStyle.Name;
                _oldSelectedIndex = _mergedUI.CbStyles.SelectedIndex;
            }
 
            _mergedUI.CbStyles.Enabled = (_mergedUI.CbStyles.Items.Count > 0);
        }
 
        void IDeviceSpecificDesigner.UseCurrentDeviceSpecificID()
        {
            if (CurrentStyle != _tmpCurrentStyle)
            {
                CurrentChoice = null;
                CurrentStyle = _tmpCurrentStyle;
            }
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  End IDeviceSpecificDesigner Implementation
        /////////////////////////////////////////////////////////////////////////
 
        private int _oldSelectedIndex;
        
        private void OnSelectedIndexChangedStylesComboBox(Object source, EventArgs e) 
        {
            if (_mergedUI.CbStyles.SelectedIndex != _oldSelectedIndex
                && !_deviceSpecificEditor.RequestRefresh())
            {
                // User needs to correct error before editing a new style.
                _mergedUI.CbStyles.SelectedIndex = _oldSelectedIndex;
                return;
            }
            
            if (_mergedUI.CbStyles.SelectedIndex >= 0)
            {
                _tmpCurrentStyle = (Style) _styleSheet[((String) _mergedUI.CbStyles.SelectedItem).ToLower(CultureInfo.InvariantCulture)];
                _deviceSpecificEditor.Refresh((String) _mergedUI.CbStyles.SelectedItem, _tmpCurrentStyle.DeviceSpecific);
            }
            _oldSelectedIndex = _mergedUI.CbStyles.SelectedIndex;
        }
 
        private void OnStyleRenamedInEditor(Object source, StyleRenamedEventArgs e)
        {
            _deviceSpecificEditor.DeviceSpecificRenamed(e.OldName, e.NewName);
        }
 
        private void OnStyleDeletedInEditor(Object source, StyleDeletedEventArgs e)
        {
            _deviceSpecificEditor.DeviceSpecificDeleted(e.Name);
        }
 
        private void OnClickEditStylesButton(Object source, EventArgs e)
        {
 
            StylesEditorDialog dialog;
            
            try
            {
                dialog = new StylesEditorDialog(
                    _styleSheet,
                    this,
                    (null != _tmpCurrentStyle) ? _tmpCurrentStyle.Name : null
                );
            }
            catch(ArgumentException ex)
            {
                Debug.Fail(ex.ToString());
                // Block user from entering StylesEditorDialog until they fix
                // duplicate style declarations.
                return;
            }
            
            StylesEditorDialog.StyleRenamedEventHandler renameHandler =
                new StylesEditorDialog.StyleRenamedEventHandler(OnStyleRenamedInEditor);
            StylesEditorDialog.StyleDeletedEventHandler deleteHandler =
                new StylesEditorDialog.StyleDeletedEventHandler(OnStyleDeletedInEditor);
            dialog.StyleRenamed += renameHandler;
            dialog.StyleDeleted += deleteHandler;
            try
            {
                _deviceSpecificEditor.BeginExternalDeviceSpecificEdit();
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    _deviceSpecificEditor.EndExternalDeviceSpecificEdit(
                        true /* commit changes */ );
                    OnInternalChange();
 
                    ((IDeviceSpecificDesigner) this).RefreshHeader(0);
                    // using mergingContext 0 because this implementation does not use the param.
                    if (_mergedUI.CbStyles.Items.Count == 0)
                    {
                        _deviceSpecificEditor.Refresh(null, null); // force the clean up and 
                        // disabling of the filter controls.
                        _tmpCurrentStyle = null;
                    }
 
                    _deviceSpecificEditor.UnderlyingObjectsChanged();
                }
                else
                {
                    _deviceSpecificEditor.EndExternalDeviceSpecificEdit(
                        false /* do not commit changes */ );
                }
            }
            finally
            {
               dialog.StyleRenamed -= renameHandler;
               dialog.StyleDeleted -= deleteHandler;
            }
        }
 
        public Style CurrentStyle
        {
           get
           {
               if (null == _currentStyle)
               {
                   // Since this property is registered to property window (from TemplateStyle),
                   // it will be accessed even before Initialize is called. In that case, 
                   // _styleSheet will be null;
                   if (_styleSheet != null && _styleSheet.Styles.Count > 0)
                   {
                       // how else can you get an entry in the Styles hashtable?
                       // this needs to be fixed once we use an ordered list of styles.
                       ICollection styles = _styleSheet.Styles;
                       foreach (String key in styles)
                       {
                           _currentStyle = (Style) _styleSheet[key];
                           Debug.Assert (_currentStyle != null);
                           break;
                       }
                   }
               }
               return _currentStyle;
           }
           set
           {
               _currentStyle = value;
           }
        }
 
        public override DeviceSpecific CurrentDeviceSpecific
        {
            get
            {
                if (null == CurrentStyle)
                {
                    return null;
                }
 
                return CurrentStyle.DeviceSpecific;
            }
        }
 
        public String TemplateStyle
        {
            get
            {
                if (null == CurrentStyle)
                {
                    return SR.GetString(SR.StyleSheet_PropNotSet);
                }
                return CurrentStyle.Name;
            }
            set
            {
                // Clear DeviceSpecificChoice of previously selected Style
                CurrentChoice = null;
                CurrentStyle = null;
                if (!String.IsNullOrEmpty(value) &&
                    !value.Equals(SR.GetString(SR.StyleSheet_PropNotSet)))
                {
                    ICollection styles = _styleSheet.Styles;
                    foreach (String key in styles)
                    {
                        Style style = (Style) _styleSheet[key];
                        if (style.Name.Equals(value))
                        {
                            CurrentStyle = style;
                            break;
                        }
                    }
                }
                // Clear DeviceSpecificChoice of currently selected Style
                CurrentChoice = null;
 
                // Invalidate the type descriptor so that the TemplateDeviceFilter gets updated
                TypeDescriptor.Refresh(Component);
            }
        }
 
        protected override void SetStyleAttributes()
        {
            Debug.Assert(Behavior != null, "Behavior is null");
 
            String marginTop = null, marginBottom = null, marginRight = null;
 
            if (ContainmentStatus == ContainmentStatus.AtTopLevel)
            {
                marginTop = "5px";
                marginBottom = "5px";
                marginRight = "30%";
            }
            else
            {
                marginTop = "3px";
                marginBottom = "3px";
                marginRight = "5px";
            }
 
            Behavior.SetStyleAttribute("marginTop", true, marginTop, true);
            Behavior.SetStyleAttribute("marginBottom", true, marginBottom, true);
            Behavior.SetStyleAttribute("marginRight", true, marginRight, true);
            Behavior.SetStyleAttribute("marginLeft", true, "5px", true);
        }
 
        protected override void PreFilterProperties(IDictionary properties) 
        {
            base.PreFilterProperties(properties);
 
            // DesignTime Property only, we will use this to select the current style.
            PropertyDescriptor designerTemplateStyleProp;
 
            designerTemplateStyleProp =
                TypeDescriptor.CreateProperty(this.GetType(), _templatesStylePropName, typeof(String),
                                     DesignerSerializationVisibilityAttribute.Hidden,
                                     MobileCategoryAttribute.Design,
                                     InTemplateMode ? ReadOnlyAttribute.Yes : ReadOnlyAttribute.No,
                                     InTemplateMode ? BrowsableAttribute.No : BrowsableAttribute.Yes,
                                     new DefaultValueAttribute(SR.GetString(SR.StyleSheet_PropNotSet)),
                                     new TypeConverterAttribute(typeof(StyleConverter)),
                                     new DescriptionAttribute(SR.GetString(SR.StyleSheet_TemplateStyleDescription)));
            properties[_templatesStylePropName] = designerTemplateStyleProp;
 
            PropertyDescriptor designerPersistedStyles;
 
            designerPersistedStyles =
                TypeDescriptor.CreateProperty(this.GetType(), _persistedStylesPropName, typeof(ICollection),
                                     //PersistenceTypeAttribute.InnerChild,
                                     PersistenceModeAttribute.InnerDefaultProperty,
                                     BrowsableAttribute.No);
            properties[_persistedStylesPropName] = designerPersistedStyles;
        }
 
        public ICollection PersistedStyles
        {
            get
            {
                Debug.Assert(null != _styleSheet, "_styleSheet is null");
                ICollection styleKeys = _styleSheet.Styles;
                ArrayList persistedStyles = new ArrayList();
                foreach (String key in styleKeys)
                {
                    Style style = _styleSheet[key];
                    persistedStyles.Add(style);
                }
                foreach (Style style in _styleSheet.DuplicateStyles)
                {
                    persistedStyles.Add(style);
                }
                return persistedStyles;
            }
        }
 
        /// <summary>
        ///    <para>
        ///       The designer's collection of verbs.
        ///    </para>
        /// </summary>
        /// <value>
        ///    <para>
        ///       An array of type <see cref='DesignerVerb'/> containing the verbs available to the
        ///       designer.
        ///    </para>
        /// </value>
        public override DesignerVerbCollection Verbs 
        {
            get 
            {
                if (_designerVerbs == null) 
                {
                    _designerVerbs = base.Verbs;
                    _designerVerbs.Add(new DesignerVerb(SR.GetString(SR.StyleSheet_StylesEditorVerb),
                                                        new EventHandler(this.OnShowStylesEditor)));
                }
                Debug.Assert(_designerVerbs.Count == 2);
 
                _designerVerbs[0].Enabled = !this.InTemplateMode;
                _designerVerbs[1].Enabled = !this.InTemplateMode;
                return _designerVerbs;
            }
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  BEGIN STYLE DESIGNER EVENTHANDLERS
        /////////////////////////////////////////////////////////////////////////
 
        protected void OnShowStylesEditor(Object sender, EventArgs e)
        {
            IComponentChangeService changeService = null;
 
            changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
            if (changeService != null) 
            {
                try 
                {
                    changeService.OnComponentChanging(_styleSheet, null);
                }
                catch (CheckoutException ex) 
                {
                    if (ex == CheckoutException.Canceled)
                    {
                        return;
                    }
                    throw;
                }
            }
 
            DialogResult result = DialogResult.Cancel;
            try 
            {
                StylesEditorDialog dialog = new StylesEditorDialog(_styleSheet, this, null);
                result = dialog.ShowDialog();
            }
            catch(ArgumentException ex)
            {
                Debug.Fail(ex.ToString());
                // Block user from entering StylesEditorDialog until they fix
                // duplicate style declarations.
            }
            finally
            {
                if (changeService != null)
                {
                    changeService.OnComponentChanged(_styleSheet, null, null, null);
 
                    if (IMobileWebFormServices != null)
                    {
                        IMobileWebFormServices.ClearUndoStack();
                    }
                }
            }
        }
 
        protected override void OnCurrentChoiceChange()
        {
            SetCurrentChoice();
            RefreshPageView();
        }
 
        private void SetCurrentChoice()
        {
            if (CurrentStyle != null && CurrentStyle.DeviceSpecific != null)
            {
                this.CurrentStyle.DeviceSpecific.SetDesignerChoice(CurrentChoice);
            }
        }
 
        private bool ValidContainment
        {
            get
            {
                return (ContainmentStatus == ContainmentStatus.AtTopLevel);
            }
        }
 
        protected override String GetErrorMessage(out bool infoMode)
        {
            infoMode = false;
 
            if (!DesignerAdapterUtil.InMobileUserControl(_styleSheet))
            {
                if (DesignerAdapterUtil.InUserControl(_styleSheet))
                {
                    infoMode = true;
                    return MobileControlDesigner._userControlWarningMessage;
                }
 
                if (!DesignerAdapterUtil.InMobilePage(_styleSheet))
                {
                    return MobileControlDesigner._mobilePageErrorMessage;
                }
            }
            
            if (!ValidContainment)
            {
                return MobileControlDesigner._topPageContainmentErrorMessage;
            }
 
            // No error condition, return null;
            return null;
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  END STYLE DESIGNER EVENTHANDLERS
        /////////////////////////////////////////////////////////////////////////
 
        [
            System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand,
            Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
        ]
        private class MergedUI : HeaderPanel
        {
            internal System.Windows.Forms.Label LblStyles;
            internal System.Windows.Forms.ComboBox CbStyles;
            internal System.Windows.Forms.Button BtnEdit;
            internal HeaderLabel LblHeader;
 
            internal MergedUI()
            {
                this.LblStyles = new System.Windows.Forms.Label();
                this.CbStyles = new System.Windows.Forms.ComboBox();
                this.BtnEdit = new System.Windows.Forms.Button();
                this.LblHeader = new HeaderLabel();
//                this.LblStyles.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
//                    | System.Windows.Forms.AnchorStyles.Right);
                this.LblStyles.Location = new System.Drawing.Point(0, 24);
                this.LblStyles.Size = new System.Drawing.Size(160, 16);
//                this.CbStyles.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
//                    | System.Windows.Forms.AnchorStyles.Right);
                this.CbStyles.DropDownWidth = 124;
                this.CbStyles.Location = new System.Drawing.Point(0, 40);
                this.CbStyles.Size = new System.Drawing.Size(160, 21);
//                this.BtnEdit.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right);
                this.BtnEdit.Location = new System.Drawing.Point(164, 39);
                this.BtnEdit.Size = new System.Drawing.Size(75, 23);
                this.LblHeader.Location = new System.Drawing.Point(0, 0);
                this.LblHeader.Size = new System.Drawing.Size(240, 16);
                this.Controls.AddRange(new System.Windows.Forms.Control[] {this.CbStyles,
                                                                           this.LblStyles,
                                                                           this.BtnEdit,
                                                                           this.LblHeader});
                this.Size = new System.Drawing.Size(240, 70);
                this.Location = new System.Drawing.Point(5,6);
            }
        }
    }
}