Home > Articles

This chapter is from the book

This chapter is from the book

Because a component is a class that's made to be integrated into a design-time host, it has a life separate from the run-time mode that we normally think of for objects. It's not enough for a component to do a good job when interacting with a user at run time as per developer instructions; a component also needs to do a good job when interacting with the developer at design time.

Hosts, Containers, and Sites

In Visual Studio .NET, the Windows Forms Designer is responsible for providing design-time services during Windows Forms development. At a high level, these services include a form's UI and code views. The responsibility of managing integration between design-time objects and the designer is handled by the designer's internal implementation of IDesignerHost (from the System.ComponentModel.Design namespace). The designer host stores IComponent references to all design-time objects on the current form and also stores the form itself (which is also a component). This collection of components is available from the IDesignerHost interface through the Container property of type IContainer (from the System.ComponentModel namespace):

interface IContainer : IDisposable {
  ComponentCollection Components { get; }
  void Add(IComponent component);
  void Add(IComponent component, string name);
  void Remove(IComponent component);
}

This implementation of IContainer allows the designer host to establish a relationship that helps it manage each of the components placed on the form. Contained components can access the designer host and each other through their container at design time. Figure 9.5 illustrates this two-way relationship.

09fig05.gifFigure 9.5. Design-Time Architecture



In Figure 9.5 you can see that the fundamental relationship between the designer host and its components is established with an implementation of the ISite interface (from the System.ComponentModel namespace):

interface ISite : IServiceProvider {
  IComponent Component { get; }
  IContainer Container { get; }
  bool DesignMode { get; }
  string Name { get; set; }
}

Internally, a container stores an array of sites. When each component is added to the container, the designer host creates a new site, connecting the component to its design-time container and vice versa by passing the ISite interface in the IComponent.Site property implementation:

interface IComponent : IDisposable {
  ISite Site { get; set; }
  event EventHandler Disposed;
}

The Component base class implements IComponent and caches the site's interface in a property. It also provides a helper property to go directly to the component's container without having to go first through the site:

class Component : MarshalByRefObject, IComponent, IDisposable {
  public IContainer Container { get; }
  public virtual ISite Site { get; set; }
  protected bool DesignMode { get; }
  protected EventHandlerList Events { get; }
}

The Component base class gives a component direct access to both the container and the site. A component can also access the Visual Studio .NET designer host itself by requesting the IDesignerHost interface from the container:

IDesignerHost designerHost = this.Container as IDesignerHost;

In Visual Studio .NET, the designer has its own implementation of the IDesignerHost interface, but, to fit into other designer hosts, it's best for a component to rely only on the interface and not on any specific implementation.

Debugging Design-Time Functionality

To demonstrate the .NET Framework's various design-time features and services, I've built a sample.3 Because components and controls share the same design-time features and because I like things that look snazzy, I built a digital/analog clock control with the following public members:

public class ClockControl : Control {
  public ClockControl();
  public DateTime Alarm { get; set; }
  public bool IsItTimeForABreak { get; set; }
  public event AlarmHandler AlarmSounded;
  ...
}

Figure 9.6 shows the control in action.

09fig06.gifFigure 9.6. Snazzy Clock Control



When you build design-time features into your components,4 you'll need to test them and, more than likely, debug them. To test run-time functionality, you simply set a breakpoint in your component's code and run a test application, relying on Visual Studio .NET to break at the right moment.

What makes testing design-time debugging different is that you need a design-time host to debug against; an ordinary application won't do. Because the hands-down hosting favorite is Visual Studio .NET itself, this means that you'll use one instance of Visual Studio .NET to debug another instance of Visual Studio .NET with a running instance of the component loaded. This may sound confusing, but it's remarkably easy to set up:

  1. Open the component solution to debug in one instance of Visual Studio .NET.

  2. Set a second instance of Visual Studio .NET as your debug application by going to Project | Properties | Configuration Properties | Debugging and setting the following properties:

    1. Set Debug Mode to Program.

    2. Set Start Application to <your devenv.exe path>\devenv.exe.

    3. Set Command Line Arguments to <your test solution path>\yourTestSolution.sln.

  3. Choose Set As StartUp Project on your component project.

  4. Set a breakpoint in the component.

  5. Use Debug | Start (F5) to begin debugging.

At this point, a second instance of Visual Studio.NET starts up with another solution, allowing you to break and debug at will, as illustrated in Figure 9.7.

09fig07.gifFigure 9.7. Design-Time Control Debugging



The key to making this setup work is to have one solution loaded in one instance of VS.NET that starts another instance of VS.NET with a completely different solution to test your component in design mode.

The DesignMode Property

To change the behavior of your component at design time, often you need to know that you're running in a Designer. For example, the clock control uses a timer component to track the time via its Tick event handler:

public class ClockControl : Control {
  ...
  Timer timer = new Timer();
   ...
   public ClockControl() {
   ...
   // Initialize timer
   timer.Interval = 1000;
   timer.Tick += new System.EventHandler(this.timer_Tick);
   timer.Enabled = true;
   }
   ...
   void timer_Tick(object sender, EventArgs e) {
   // Refresh clock face
   this.Invalidate();
   ...
   }
   }
   

Inspection reveals that the control is overly zealous in keeping time both at design time and at run time. Such code should really be executed at run time only. In this situation, a component or control can check the DesignMode property, which is true only when it is executing at design time. The timer_Tick event handler can use DesignMode to ensure that it is executed only at run time, returning immediately from the event handler otherwise:

void timer_Tick(object sender, EventArgs e) {
  // Don't execute event if running in design time
  if( this.DesignMode ) return;
   this.Invalidate();
   ...
   }
   

Note that the DesignMode property should not be checked from within the constructor or from any code that the constructor calls. A constructor is called before a control is sited, and it's the site that determines whether or not a control is in design mode. DesignMode will also be false in the constructor.

Attributes

Design-time functionality is available to controls in one of two ways: programmatically and declaratively. Checking the DesignMode property is an example of the programmatic approach. One side effect of using a programmatic approach is that your implementation takes on some of the design-time responsibility, resulting in a blend of design-time and run-time code within the component implementation.

The declarative approach, on the other hand, relies on attributes to request design-time functionality implemented somewhere else, such as the designer host. For example, consider the default Toolbox icon for a component, as shown in Figure 9.8.

09fig08.gifFigure 9.8. Default Toolbox Icon



If the image is important to your control, you'll want to change the icon to something more appropriate. The first step is to add a 16×16, 16-color icon or bitmap to your project and set its Build Action to Embedded Resource (embedded resources are discussed in Chapter 10: Resources). Then add the ToolboxBitmapAttribute to associate the icon with your component:

[ToolboxBitmapAttribute(
   typeof(ClockControlLibrary.ClockControl),
   "images.ClockControl.ico")]
   public class ClockControl : Control {...}
   

The parameters to this attribute specify the use of an icon resource located in the "images" project subfolder.

You'll find that the Toolbox image doesn't change if you add or change ToolboxBitmapAttribute after the control has been added to the Toolbox. However, if your implementation is a component, its icon is updated in the component tray. One can only assume that the Toolbox is not under the direct management of the Windows Form Designer, whereas the component tray is. To refresh the Toolbox, remove your component and then add it again to the Toolbox. The result will be something like Figure 9.9.

09fig09.gifFigure 9.9. New and Improved Toolbox Icon



You can achieve the same result without using ToolboxBitmapAttribute: Simply place a 16×16, 16-color bitmap in the same project folder as the component, and give it the same name as the component class. This is a special shortcut for the ToolboxBitmapAttribute only; don't expect to find similar shortcuts for other design-time attributes.

Property Browser Integration

No matter what the icon is, after a component is dragged from the Toolbox onto a form, it can be configured through the designer-managed Property Browser. The Designer uses reflection to discover which properties the design-time control instance exposes. For each property, the Designer calls the associated get accessor for its current value and renders both the property name and the value onto the Property Browser. Figure 9.10 shows how the Property Browser looks for the basic clock control.

09fig10.gifFigure 9.10. Visual Studio.NET with a Clock Control Chosen



The System.ComponentModel namespace provides a comprehensive set of attributes, shown in Table 9.1, to help you modify your component's behavior and appearance in the Property Browser.

By default, public read and read/write properties—such as the Alarm property highlighted in Figure 9.10—are displayed in the Property Browser under the "Misc" category. If a property is intended for run time only, you can prevent it from appearing in the Property Browser by adorning the property with BrowsableAttribute:

[BrowsableAttribute(false)]
   public bool IsItTimeForABreak {
   get { ... }
   set { ... }
   }
   

With IsItTimeForABreak out of the design-time picture, only the custom Alarm property remains. However, it's currently listed under the Property Browser's Misc category and lacks a description. You can improve the situation by applying both CategoryAttribute and DescriptionAttribute:

[
   CategoryAttribute("Behavior"),
   DescriptionAttribute("Alarm for late risers")
   ]
   public DateTime Alarm {
   get { ... }
   set { ... }
   }
   

Table 9.1. Design-Time Property Browser Attributes

Attribute

Description

AmbientValueAttribute

Specifies the value for this property that causes it to acquire its value from another source, usually its container (see the section titled Ambient Properties in Chapter 8: Controls).

BrowsableAttribute

Determines whether the property is visible in the Property Browser.

CategoryAttribute

Tells the Property Browser which group to include this property in.

DescriptionAttribute

Provides text for the Property Browser to display in its description bar.

DesignOnlyAttribute

Specifies that the design-time value of this property is serialized to the form's resource file. This attribute is typically used on properties that do not exist at run time.

MergablePropertyAttribute

Allows this property to be combined with properties from other objects when more than one are selected and edited.

ParenthesizePropertyNameAttribute

Specifies whether this property should be surrounded by parentheses in the Property Browser.

ReadOnlyAttribute

Specifies that this property cannot be edited in the Property Browser.

After adding these attributes and rebuilding, you will notice that the Alarm property has relocated to the desired category in the Property Browser, and the description appears on the description bar when you select the property (both shown in Figure 9.11). You can actually use CategoryAttribute to create new categories, but you should do so only if the existing categories don't suitably describe a property's purpose. Otherwise, you'll confuse users looking for your properties in the logical category.

09fig11.gifFigure 9.11. Alarm Property with CategoryAttribute and DescriptionAttribute Applied



In Figure 9.11, some property values are shown in boldface and others are not. Boldface values are those that differ from the property's default value, which is specified by DefaultValueAttribute:

 [
  CategoryAttribute("Appearance"),
  DescriptionAttribute("Whether digital time is shown"),
  DefaultValueAttribute(true)
   ]
   public bool ShowDigitalTime {
   get { ... }
   set { ... }
   }
   

Using DefaultValueAttribute also allows you to reset a property to its default value using the Property Browser, which is available from the property's context menu, as shown in Figure 9.12.

09fig12.gifFigure 9.12. Resetting a Property to Its Default Value



This option is disabled if the current property is already the default value. Default values represent the most common value for a property. Some properties, such as Alarm or Text, simply don't have a default that's possible to define, whereas others, such as Enabled and ControlBox, do.

Just like properties, a class can have defaults. You can specify a default event by adorning a class with DefaultEventAttribute:

[DefaultEventAttribute("AlarmSounded")]
   class ClockControl : Control { ... }
   

Double-clicking the component causes the Designer to automatically hook up the default event; it does this by serializing code to register with the specified event in InitializeComponent and providing a handler for it:

class ClockControlHostForm : Form {
  ...
  void InitializeComponent() {
    ...
    this.clockControl1.AlarmSounded +=
   new AlarmHandler(this.clockControl1_AlarmSounded);
   ...
   }
   ...
   void clockControl1_AlarmSounded(
   object sender,
   ClockControlLibrary.AlarmType type) {
   }
   ...
   }
   

You can also adorn your component with DefaultPropertyAttribute:

[DefaultPropertyAttribute("ShowDigitalTime")]
   public class ClockControl : Windows.Forms.Control { ... }
   

This attribute causes the Designer to highlight the default property when the component's property is first edited, as shown in Figure 9.13.

09fig13.gifFigure 9.13. Default Property Highlighted in the Property Browser



Default properties aren't terribly useful, but setting the correct default event properly can save a developer's time when using your component.

Code Serialization

Whereas DefaultEventAttribute and DefaultPropertyAttribute affect the behavior only of the Property Browser, DefaultValueAttribute serves a dual purpose: It also plays a role in helping the Designer determine which code is serialized to InitializeComponent. Properties that don't have a default value are automatically included in InitializeComponent. Those that have a default value are included only if the property's value differs from the default. To avoid unnecessarily changing a property, your initial property values should match the value set by DefaultValueAttribute.

DesignerSerializationVisibilityAttribute is another attribute that affects the code serialization process. The DesignerSerializationVisibilityAttribute constructor takes a value from the DesignerSerializationVisibility enumeration:

enum DesignerSerializationVisibility {
  Visible, // initialize this property if nondefault value
  Hidden, // don't initialize this property
  Content // initialize sets of properties on a subobject
}

The default, Visible, causes a property's value to be set in InitializeComponent if the value of the property is not the same as the value of the default. If you'd prefer that no code be generated to initialize a property, use Hidden:

[
  DefaultValueAttribute(true),
  DesignerSerializationVisibilityAttribute(
   DesignerSerializationVisibility.Hidden)
   ]
   public bool ShowDigitalTime {
   get { ... }
   set { ... }
   }
   

You can use Hidden in conjunction with BrowsableAttribute set to false for run-time-only properties. Although BrowsableAttribute determines whether a property is visible in the Property Browser, its value may still be serialized unless you prevent that by using Hidden.

By default, properties that maintain a collection of custom types cannot be serialized to code. Such a property is implemented by the clock control in the form of a "messages to self" feature, which captures a set of messages and displays them at the appropriate date and time. To enable serialization of a collection, you can apply DesignerSerializationVisibility. Content to instruct the Designer to walk into the property and serialize its internal structure:

[
  CategoryAttribute("Behavior"),
  DescriptionAttribute ("Stuff to remember for later"),
  DesignerSerializationVisibilityAttribute
   (DesignerSerializationVisibility.Content)
   ]
   public MessageToSelfCollection MessagesToSelf {
   get { ... }
   set { ... }
   }
   

The generated InitializeComponent code for a single message looks like this:

void InitializeComponent() {
  ...
  this.clockControl1.MessagesToSelf.AddRange(
   new ClockControlLibrary.MessageToSelf[] {
   new ClockControlLibrary.MessageToSelf(
   new System.DateTime(2003, 2, 22, 21, 55, 0, 0), "Wake up")});
   ...
   }
   

This code also needs a "translator" class to help the Designer serialize the code to construct a MessageToSelf type. This is covered in detail in the section titled "Type Converters" later in this chapter.

Host Form Integration

While we're talking about affecting code serialization, there's another trick that's needed for accessing a component's hosting form. For example, consider a clock control and a clock component, both of which offer the ability to place the current time in the hosting form's caption. Each needs to acquire a reference to the host form to set the time in the form's Text property. The control comes with native support for this requirement:

Form hostingForm = this.Parent as Form;

Unfortunately, components do not provide a similar mechanism to access their host form. At design time, the component can find the form in the designer host's Container collection. However, this technique will not work at run time because the Container is not available at run time. To get its container at run time, a component must take advantage of the way the Designer serializes code to the InitializeComponent method. You can write code that takes advantage of this infrastructure to seed itself with a reference to the host form at design time and run time. The first step is to grab the host form at design time using a property of type Form:

Form hostingForm = null;

[BrowsableAttribute(false)]
public Form HostingForm {
  // Used to populate InitializeComponent at design time
  get {
    if( (hostingForm == null) && this.DesignMode ) {
     // Access designer host and obtain reference to root component
      IDesignerHost designer =
        this.GetService(typeof(IDesignerHost)) as IDesignerHost;
      if( designer != null ) {
        hostingForm = designer.RootComponent as Form;
      }
    }
    return hostingForm;
  }

  set {...}
}

The HostingForm property is used to populate the code in InitializeComponent at design time, when the designer host is available. Stored in the designer host's RootComponent property, the root component represents the primary purpose of the Designer. For example, a Form component is the root component of the Windows Forms Designer. DesignerHost.RootComponent is a helper function that allows you to access the root component without enumerating the Container collection. Only one component is considered the root component by the designer host. Because the HostingForm property should go about its business transparently, you should decorate it with BrowsableAttribute set to false, thereby ensuring that the property is not editable from the Property Browser.

Because HostForm is a public property, the Designer retrieves HostForm's value at design time to generate the following code, which is needed to initialize the component:

void InitializeComponent() {
  ...
  this.myComponent1.HostingForm = this;
   ...
   }
   

At run time, when InitializeComponent runs, it will return the hosting form to the component via the HostingForm property setter:

Form hostingForm = null;

[BrowsableAttribute(false)]
public Form HostingForm {
  get { ... }

  // Set by InitializeComponent at run time
   set {
   if( !this.DesignMode ) {
   // Don't change hosting form at run time
   if( (hostingForm != null) && (hostingForm != value) ) {
   throw new
   InvalidOperationException
   ("Can't set HostingForm at run time.");
   }
   }
   else hostingForm = value;
   }
   }
   

In this case, we're using our knowledge of how the Designer works to trick it into handing our component a value at run-time that we pick at design-time.

Batch Initialization

As you may have noticed, the code that eventually gets serialized to InitializeComponent is laid out as an alphanumerically ordered sequence of property sets, grouped by object. Order isn't important until your component exposes range-dependent properties, such as Min/Max or Start/Stop pairs. For example, the clock control also has two dependent properties: PrimaryAlarm and BackupAlarm (the Alarm property was split into two for extra sleepy people).

Internally, the clock control instance initializes the two properties 10 minutes apart, starting from the current date and time:

DateTime primaryAlarm = DateTime.Now;
DateTime backupAlarm = DateTime.Now.AddMinutes(10);

Both properties should check to ensure that the values are valid:

public DateTime PrimaryAlarm {
  get { return primaryAlarm; }
  set {
    if( value >= backupAlarm )
   throw new ArgumentOutOfRangeException
   ("Primary alarm must be before Backup alarm");
   primaryAlarm = value;
   }
   }
   
   public DateTime BackupAlarm {
   get { return backupAlarm; }
   set {
   if( value < primaryAlarm )
   throw new ArgumentOutOfRangeException
   ("Backup alarm must be after Primary alarm");
   backupAlarm = value;
   }
   }
   

With this dependence checking in place, at design time the Property Browser will show an exception in an error dialog if an invalid property is entered, as shown in Figure 9.14.

09fig14.gifFigure 9.14. Invalid Value Entered into the Property Browser



This error dialog is great at design time, because it lets the developer know the relationship between the two properties. However, there's a problem when the properties are serialized into InitializeComponent alphabetically:

void InitializeComponent() {
  ...
  // clockControl1
  this.clockControl1.BackupAlarm =
   new System.DateTime(2003, 11, 24, 13, 42, 47, 46);
   ...
   this.clockControl1.PrimaryAlarm =
   new System.DateTime(2003, 11, 24, 13, 57, 47, 46);
   ...
   }
   

Notice that even if the developer sets the two alarms properly, as soon as BackupAlarm is set and is checked against the default value of PrimaryAlarm, a run-time exception will result.

To avoid this, a component must be notified when its properties are being set from InitializeComponent in "batch mode" so that they can be validated all at once at the end. Implementing the ISupportInitialize interface (from the System.ComponentModel namespace) provides this capability, with two notification methods to be called before and after initialization:

public interface ISupportInitialize {
  public void BeginInit();

  public void EndInit();
}

When a component implements this interface, calls to BeginInit and EndInit are serialized to InitializeComponent:

void InitializeComponent() {
  ...
  ((System.ComponentModel.ISupportInitialize)
   (this.clockControl1)).BeginInit();
   ...
   // clockControl1
   this.clockControl1.BackupAlarm =
   new System.DateTime(2003, 11, 24, 13, 42, 47, 46);
   ...
   this.clockControl1.PrimaryAlarm =
   new System.DateTime(2003, 11, 24, 13, 57, 47, 46);
   ...
   ((System.ComponentModel.ISupportInitialize)
   (this.clockControl1)).EndInit();
   ...
   }
   

The call to BeginInit signals the entry into initialization batch mode, a signal that is useful for turning off value checking:

public class ClockControl : Control, ISupportInitialize {
   ...
   bool initializing = false;
   ...
   void BeginInit() { initializing = true; }
   ...
   public DateTime PrimaryAlarm {
   get { ... }
   set {
   if( !initializing ) { /* check value */ }
   primaryAlarm = value;
   }
   }
   
   public DateTime BackupAlarm {
   get { ... }
   set {
   if( !initializing ) { /* check value */ }
   backupAlarm = value;
   }
   }
   }
   

Placing the appropriate logic into EndInit performs batch validation:

public class ClockControl : Control, ISupportInitialize {
   void EndInit() {
   if( primaryAlarm >= backupAlarm )
   throw new ArgumentOutOfRangeException
   ("Primary alarm must be before Backup alarm");
   }
   ...
   }
   

EndInit also turns out to be a better place to avoid the timer's Tick event, which currently fires once every second during design time. Although the code inside the Tick event handler doesn't run at design time (because it's protected by a check of the DesignMode property), it would be better not to even start the timer at all until run time. However, because DesignMode can't be checked in the constructor, a good place to check it is in the EndInit call, which is called after all properties have been initialized at run time or at design time:

public class ClockControl : Control, ISupportInitialize {
  ...
  void EndInit() {
    ...
    if( !this.DesignMode ) {
   // Initialize timer
   timer.Interval = 1000;
   timer.Tick += new System.EventHandler(this.timer_Tick);
   timer.Enabled = true;
   }
   }
   }
   

The Designer and the Property Browser provide all kinds of design-time help to augment the experience of developing a component, including establishing how a property is categorized and described to the developer and how it's serialized for the InitializeComponent method.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020