1 //------------------------------------------------------------------------------ 2 // <copyright file="MobileControlDesigner.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 namespace System.Web.UI.Design.MobileControls 8 { 9 using System; 10 using System.ComponentModel; 11 using System.ComponentModel.Design; 12 using System.Diagnostics; 13 using System.Drawing; 14 using System.Drawing.Design; 15 using System.Collections; 16 using System.Globalization; 17 using System.IO; 18 using System.Reflection; 19 using System.Web.UI; 20 using System.Web.UI.Design; 21 using System.Web.UI.Design.MobileControls.Adapters; 22 using System.Web.UI.Design.MobileControls.Util; 23 using System.Web.UI.MobileControls; 24 25 [ 26 System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, 27 Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) 28 ] 29 [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.")] 30 internal class MobileControlDesigner : 31 ControlDesigner, IMobileDesigner, IDeviceSpecificDesigner 32 { 33 private bool _containmentStatusDirty = true; 34 private ContainmentStatus _containmentStatus; 35 private IDesignerHost _host; 36 private IWebFormsDocumentService _iWebFormsDocumentService; 37 private IMobileWebFormServices _iMobileWebFormServices; 38 private MobileControl _mobileControl; 39 private System.Windows.Forms.Control _header; 40 41 internal static readonly String resourceDllUrl = 42 "res://" + typeof(MobileControlDesigner).Module.FullyQualifiedName; 43 44 internal static readonly String errorIcon = 45 resourceDllUrl + "//ERROR_GIF"; 46 47 internal static readonly String infoIcon = 48 resourceDllUrl + "//INFO_GIF"; 49 50 internal static readonly String defaultErrorDesignTimeHTML = 51 @" 52 <table cellpadding=2 cellspacing=0 width='{4}' 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'> 53 <tr><td><span style='font-weight:bold'> {0}</span> - {1}</td></tr> 54 <tr><td> 55 <table style='font-family:tahoma;font-size:8pt;color:window;background-color:ButtonShadow'> 56 <tr> 57 <td valign='top'><img src={3} /></td> 58 <td width='100%'>{2}</td> 59 </tr> 60 </table> 61 </td></tr> 62 </table> 63 "; 64 65 internal static readonly String _formPanelContainmentErrorMessage = 66 SR.GetString(SR.MobileControl_FormPanelContainmentErrorMessage); 67 68 internal static readonly String _mobilePageErrorMessage = 69 SR.GetString(SR.MobileControl_MobilePageErrorMessage); 70 71 internal static readonly String _topPageContainmentErrorMessage = 72 SR.GetString(SR.MobileControl_TopPageContainmentErrorMessage); 73 74 internal static readonly String _userControlWarningMessage = 75 SR.GetString(SR.MobileControl_UserControlWarningMessage); 76 77 private const String _appliedDeviceFiltersPropName = "AppliedDeviceFilters"; 78 private const String _propertyOverridesPropName = "PropertyOverrides"; 79 private const String _defaultDeviceSpecificIdentifier = "unique"; 80 81 private static readonly string[] _nonBrowsableProperties = new string[] { 82 "EnableTheming", 83 "Expressions", 84 "SkinID", 85 }; 86 87 // predefined constants used for mergingContext 88 internal const int MergingContextChoices = 0; 89 internal const int MergingContextTemplates = 1; 90 internal const int MergingContextProperties = 2; 91 92 [ 93 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 94 Editor(typeof(AppliedDeviceFiltersTypeEditor), typeof(UITypeEditor)), 95 MergableProperty(false), 96 MobileCategory(SR.Category_DeviceSpecific), 97 MobileSysDescription(SR.MobileControl_AppliedDeviceFiltersDescription), 98 ParenthesizePropertyName(true), 99 ] 100 protected String AppliedDeviceFilters 101 { 102 get 103 { 104 return String.Empty; 105 } 106 } 107 108 protected ContainmentStatus ContainmentStatus 109 { 110 get 111 { 112 if (!_containmentStatusDirty) 113 { 114 return _containmentStatus; 115 } 116 117 _containmentStatus = 118 DesignerAdapterUtil.GetContainmentStatus(_mobileControl); 119 120 _containmentStatusDirty = false; 121 return _containmentStatus; 122 } 123 } 124 125 internal Object DesignTimeElementInternal 126 { 127 get 128 { 129 return typeof(HtmlControlDesigner).InvokeMember("DesignTimeElement", 130 BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic, 131 null, this, null, CultureInfo.InvariantCulture); 132 } 133 } 134 135 public override bool DesignTimeHtmlRequiresLoadComplete 136 { 137 get 138 { 139 return true; 140 } 141 } 142 143 private IDesignerHost Host 144 { 145 get 146 { 147 if (_host != null) 148 { 149 return _host; 150 } 151 _host = (IDesignerHost)GetService(typeof(IDesignerHost)); 152 Debug.Assert(_host != null); 153 return _host; 154 } 155 } 156 157 internal IMobileWebFormServices IMobileWebFormServices 158 { 159 get 160 { 161 if (_iMobileWebFormServices == null) 162 { 163 _iMobileWebFormServices = 164 (IMobileWebFormServices)GetService(typeof(IMobileWebFormServices)); 165 } 166 167 return _iMobileWebFormServices; 168 } 169 } 170 171 private IWebFormsDocumentService IWebFormsDocumentService 172 { 173 get 174 { 175 if (_iWebFormsDocumentService == null) 176 { 177 _iWebFormsDocumentService = 178 (IWebFormsDocumentService)GetService(typeof(IWebFormsDocumentService)); 179 180 Debug.Assert(_iWebFormsDocumentService != null); 181 } 182 183 return _iWebFormsDocumentService; 184 } 185 } 186 187 /// <summary> 188 /// Indicates whether the initial page load is completed 189 /// </summary> 190 protected bool LoadComplete 191 { 192 get 193 { 194 return !IWebFormsDocumentService.IsLoading; 195 } 196 } 197 198 [ 199 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 200 Editor(typeof(PropertyOverridesTypeEditor), typeof(UITypeEditor)), 201 MergableProperty(false), 202 MobileCategory(SR.Category_DeviceSpecific), 203 MobileSysDescription(SR.MobileControl_DeviceSpecificPropsDescription), 204 ParenthesizePropertyName(true), 205 ] 206 protected String PropertyOverrides 207 { 208 get 209 { 210 return String.Empty; 211 } 212 } 213 214 private bool ValidContainment 215 { 216 get 217 { 218 return ( 219 ContainmentStatus == ContainmentStatus.InForm || 220 ContainmentStatus == ContainmentStatus.InPanel || 221 ContainmentStatus == ContainmentStatus.InTemplateFrame); 222 } 223 } 224 GetErrorMessage(out bool infoMode)225 protected virtual String GetErrorMessage(out bool infoMode) 226 { 227 infoMode = false; 228 229 // Skip containment checking if the control is placed in MobileUserControl 230 if (!DesignerAdapterUtil.InMobileUserControl(_mobileControl)) 231 { 232 if (DesignerAdapterUtil.InUserControl(_mobileControl)) 233 { 234 infoMode = true; 235 return MobileControlDesigner._userControlWarningMessage; 236 } 237 238 if (!DesignerAdapterUtil.InMobilePage(_mobileControl)) 239 { 240 return _mobilePageErrorMessage; 241 } 242 243 if (!ValidContainment) 244 { 245 return _formPanelContainmentErrorMessage; 246 } 247 } 248 249 bool containsTag; 250 bool containsDataboundLiteral; 251 _mobileControl.GetControlText(out containsTag, out containsDataboundLiteral); 252 253 if (containsTag) 254 { 255 return SR.GetString(SR.MobileControl_InnerTextCannotContainTagsDesigner); 256 } 257 258 // Containment is valid, return null; 259 return null; 260 } 261 262 /// <summary> 263 /// <para> 264 /// Gets the HTML to be used for the design time representation of the control runtime. 265 /// </para> 266 /// </summary> 267 /// <returns> 268 /// <para> 269 /// The design time HTML. 270 /// </para> 271 /// </returns> GetDesignTimeHtml()272 public sealed override String GetDesignTimeHtml() 273 { 274 if (!LoadComplete) 275 { 276 return null; 277 } 278 279 bool infoMode = false; 280 String errorMessage = GetErrorMessage(out infoMode); 281 SetStyleAttributes(); 282 283 if (null != errorMessage) 284 { 285 return GetDesignTimeErrorHtml(errorMessage, infoMode); 286 } 287 288 String designTimeHTML = null; 289 try 290 { 291 designTimeHTML = GetDesignTimeNormalHtml(); 292 } 293 catch (Exception ex) 294 { 295 Debug.Fail(ex.ToString()); 296 designTimeHTML = GetDesignTimeErrorHtml(ex.Message, false); 297 } 298 299 return designTimeHTML; 300 } 301 GetDesignTimeNormalHtml()302 protected virtual String GetDesignTimeNormalHtml() 303 { 304 return GetEmptyDesignTimeHtml(); 305 } 306 307 /// <summary> 308 /// <para> 309 /// Gets the HTML to be used at design time as the representation of the 310 /// control when the control runtime does not return any rendered 311 /// HTML. The default behavior is to return a string containing the name 312 /// of the component. 313 /// </para> 314 /// </summary> 315 /// <returns> 316 /// <para> 317 /// The name of the component, by default. 318 /// </para> 319 /// </returns> GetEmptyDesignTimeHtml()320 protected override String GetEmptyDesignTimeHtml() 321 { 322 return "<div style='width:100%'>" + base.GetEmptyDesignTimeHtml() + "</div>"; 323 } 324 GetErrorDesignTimeHtml(Exception e)325 protected override sealed String GetErrorDesignTimeHtml(Exception e) 326 { 327 return base.GetErrorDesignTimeHtml(e); 328 } 329 330 /// <summary> 331 /// 332 /// </summary> GetDesignTimeErrorHtml(String errorMessage, bool infoMode)333 protected virtual String GetDesignTimeErrorHtml(String errorMessage, bool infoMode) 334 { 335 return DesignerAdapterUtil.GetDesignTimeErrorHtml( 336 errorMessage, infoMode, _mobileControl, Behavior, ContainmentStatus); 337 } 338 339 /// <summary> 340 /// <para> 341 /// Gets the HTML to be persisted for the content present within the associated server control runtime. 342 /// </para> 343 /// </summary> 344 /// <returns> 345 /// <para> 346 /// Persistable Inner HTML. 347 /// </para> 348 /// </returns> GetPersistInnerHtml()349 public override String GetPersistInnerHtml() 350 { 351 if (!IsDirty) 352 { 353 // Returning a null string will prevent the actual save. 354 return null; 355 } 356 357 StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); 358 359 // HACK ALERT: 360 // We need to temporarily swap out the Text property to avoid being 361 // persisted into control inner text. However, setting the Text property 362 // will wipe out all control collections, therefore we need to cache 363 // the Text value too. 364 bool hasControls = _mobileControl.HasControls(); 365 if ((_mobileControl is TextControl || _mobileControl is TextView) 366 && hasControls) 367 { 368 String originalText = null; 369 Control[] children = null; 370 371 // Cache all child controls here. 372 children = new Control[_mobileControl.Controls.Count]; 373 _mobileControl.Controls.CopyTo(children, 0); 374 375 // Replace the text with empty string. 376 if (_mobileControl is TextControl) 377 { 378 originalText = ((TextControl)_mobileControl).Text; 379 ((TextControl)_mobileControl).Text = String.Empty; 380 } 381 else 382 { 383 originalText = ((TextView)_mobileControl).Text; 384 ((TextView)_mobileControl).Text = String.Empty; 385 } 386 387 try 388 { 389 // Persist inner properties without Text property. 390 MobileControlPersister.PersistInnerProperties(sw, _mobileControl, Host); 391 // Persist the child collections. 392 foreach (Control c in children) 393 { 394 MobileControlPersister.PersistControl(sw, c, Host); 395 } 396 } 397 finally 398 { 399 // Write the original text back to control. 400 if (_mobileControl is TextControl) 401 { 402 ((TextControl)_mobileControl).Text = originalText; 403 } 404 else 405 { 406 ((TextView)_mobileControl).Text = originalText; 407 } 408 409 // Add the child controls back. 410 foreach (Control c in children) 411 { 412 _mobileControl.Controls.Add(c); 413 } 414 } 415 } 416 else 417 { 418 MobileControlPersister.PersistInnerProperties(sw, _mobileControl, Host); 419 } 420 421 IsDirty = false; 422 return sw.ToString(); 423 } 424 425 /// <summary> 426 /// <para> 427 /// Initializes the designer with the component for design. 428 /// </para> 429 /// </summary> 430 /// <param name='component'> 431 /// The control element for design. 432 /// </param> 433 /// <remarks> 434 /// <para> 435 /// This is called by the designer host to establish the component for 436 /// design. 437 /// </para> 438 /// </remarks> 439 /// <seealso cref='System.ComponentModel.Design.IDesigner'/> Initialize(IComponent component)440 public override void Initialize(IComponent component) 441 { 442 Debug.Assert(component is System.Web.UI.MobileControls.MobileControl, 443 "MobileControlDesigner.Initialize - Invalid MobileControl Control"); 444 445 base.Initialize(component); 446 447 _mobileControl = (System.Web.UI.MobileControls.MobileControl) component; 448 } 449 SetStyleAttributes()450 protected virtual void SetStyleAttributes() 451 { 452 //Debug.Assert(Behavior != null, "Behavior is null, Load completed? " + LoadComplete.ToString()); 453 454 DesignerAdapterUtil.SetStandardStyleAttributes(Behavior, ContainmentStatus); 455 } 456 OnComponentChanged(Object sender, ComponentChangedEventArgs ce)457 public override void OnComponentChanged(Object sender, ComponentChangedEventArgs ce) 458 { 459 // Delegate to the base class implementation first! 460 base.OnComponentChanged(sender, ce); 461 462 MemberDescriptor member = ce.Member; 463 if (member != null && 464 member.GetType().FullName.Equals(Constants.ReflectPropertyDescriptorTypeFullName)) 465 { 466 PropertyDescriptor propDesc = (PropertyDescriptor)member; 467 String propName = propDesc.Name; 468 469 if ((_mobileControl is TextControl || _mobileControl is TextView) 470 && propName.Equals("Text")) 471 { 472 _mobileControl.Controls.Clear(); 473 } 474 } 475 } 476 477 /// <summary> 478 /// <para> 479 /// Notification that is called when the associated control is parented. 480 /// </para> 481 /// </summary> OnSetParent()482 public override void OnSetParent() 483 { 484 base.OnSetParent(); 485 486 _containmentStatusDirty = true; 487 if (LoadComplete) 488 { 489 UpdateRendering(); 490 } 491 } 492 PreFilterProperties(IDictionary properties)493 protected override void PreFilterProperties(IDictionary properties) 494 { 495 base.PreFilterProperties(properties); 496 497 properties[_appliedDeviceFiltersPropName] = 498 TypeDescriptor.CreateProperty(this.GetType(), _appliedDeviceFiltersPropName, typeof(String)); 499 500 properties[_propertyOverridesPropName] = 501 TypeDescriptor.CreateProperty(this.GetType(), _propertyOverridesPropName, typeof(String)); 502 503 foreach (string propertyName in _nonBrowsableProperties) { 504 PropertyDescriptor property = (PropertyDescriptor) properties[propertyName]; 505 Debug.Assert(property != null, "Property is null: " + propertyName); 506 if (property != null) { 507 properties[propertyName] = TypeDescriptor.CreateProperty(this.GetType(), property, BrowsableAttribute.No); 508 } 509 } 510 } 511 512 /* 513 * IMobileDesigner INTERFACE IMPLEMENTATION 514 */ 515 516 /// <summary> 517 /// 518 /// </summary> UpdateRendering()519 public void UpdateRendering() 520 { 521 _mobileControl.RefreshStyle(); 522 523 UpdateDesignTimeHtml(); 524 } 525 526 //////////////////////////////////////////////////////////////////////// 527 // Begin IDeviceSpecificDesigner Implementation 528 //////////////////////////////////////////////////////////////////////// 529 IDeviceSpecificDesigner.SetDeviceSpecificEditor(IRefreshableDeviceSpecificEditor editor)530 void IDeviceSpecificDesigner.SetDeviceSpecificEditor 531 (IRefreshableDeviceSpecificEditor editor) 532 { 533 } 534 535 String IDeviceSpecificDesigner.CurrentDeviceSpecificID 536 { 537 get 538 { 539 return _defaultDeviceSpecificIdentifier; 540 } 541 } 542 543 System.Windows.Forms.Control IDeviceSpecificDesigner.Header 544 { 545 get 546 { 547 return _header; 548 } 549 } 550 551 System.Web.UI.Control IDeviceSpecificDesigner.UnderlyingControl 552 { 553 get 554 { 555 return _mobileControl; 556 } 557 } 558 559 Object IDeviceSpecificDesigner.UnderlyingObject 560 { 561 get 562 { 563 return _mobileControl; 564 } 565 } 566 IDeviceSpecificDesigner.InitHeader(int mergingContext)567 void IDeviceSpecificDesigner.InitHeader(int mergingContext) 568 { 569 HeaderPanel panel = new HeaderPanel(); 570 HeaderLabel lblDescription = new HeaderLabel(); 571 572 lblDescription.TabIndex = 0; 573 lblDescription.Text = SR.GetString( 574 SR.MobileControl_SettingGenericChoiceDescription 575 ); 576 panel.Height = lblDescription.Height; 577 panel.Width = lblDescription.Width; 578 panel.Controls.Add(lblDescription); 579 _header = panel; 580 } 581 IDeviceSpecificDesigner.RefreshHeader(int mergingContext)582 void IDeviceSpecificDesigner.RefreshHeader(int mergingContext) 583 { 584 } 585 IDeviceSpecificDesigner.GetDeviceSpecific(String deviceSpecificParentID, out DeviceSpecific ds)586 bool IDeviceSpecificDesigner.GetDeviceSpecific(String deviceSpecificParentID, out DeviceSpecific ds) 587 { 588 Debug.Assert(_defaultDeviceSpecificIdentifier == deviceSpecificParentID); 589 ds = ((MobileControl) _mobileControl).DeviceSpecific; 590 return true; 591 } 592 IDeviceSpecificDesigner.SetDeviceSpecific(String deviceSpecificParentID, DeviceSpecific ds)593 void IDeviceSpecificDesigner.SetDeviceSpecific(String deviceSpecificParentID, DeviceSpecific ds) 594 { 595 Debug.Assert(_defaultDeviceSpecificIdentifier == deviceSpecificParentID); 596 if (null != ds) 597 { 598 ds.SetOwner((MobileControl) _mobileControl); 599 } 600 _mobileControl.DeviceSpecific = ds; 601 } 602 IDeviceSpecificDesigner.UseCurrentDeviceSpecificID()603 void IDeviceSpecificDesigner.UseCurrentDeviceSpecificID() 604 { 605 } 606 607 //////////////////////////////////////////////////////////////////////// 608 // End IDeviceSpecificDesigner Implementation 609 //////////////////////////////////////////////////////////////////////// 610 } 611 } 612